Commit | Line | Data |
---|---|---|
49626e3d CW |
1 | <?php |
2 | /* | |
3 | +--------------------------------------------------------------------+ | |
2fe49090 | 4 | | CiviCRM version 5 | |
49626e3d | 5 | +--------------------------------------------------------------------+ |
6b83d5bd | 6 | | Copyright CiviCRM LLC (c) 2004-2019 | |
49626e3d CW |
7 | +--------------------------------------------------------------------+ |
8 | | This file is a part of CiviCRM. | | |
9 | | | | |
10 | | CiviCRM is free software; you can copy, modify, and distribute it | | |
11 | | under the terms of the GNU Affero General Public License | | |
12 | | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. | | |
13 | | | | |
14 | | CiviCRM is distributed in the hope that it will be useful, but | | |
15 | | WITHOUT ANY WARRANTY; without even the implied warranty of | | |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | | |
17 | | See the GNU Affero General Public License for more details. | | |
18 | | | | |
19 | | You should have received a copy of the GNU Affero General Public | | |
20 | | License along with this program; if not, contact CiviCRM LLC | | |
21 | | at info[AT]civicrm[DOT]org. If you have questions about the | | |
22 | | GNU Affero General Public License or the licensing of CiviCRM, | | |
23 | | see the CiviCRM license FAQ at http://civicrm.org/licensing | | |
24 | +--------------------------------------------------------------------+ | |
d25dd0ee | 25 | */ |
49626e3d | 26 | |
83479334 TO |
27 | /** |
28 | * Verify that the REST API bindings correctly parse and authenticate requests. | |
6769c30e TO |
29 | * |
30 | * @group e2e | |
83479334 | 31 | */ |
6769c30e | 32 | class E2E_Extern_RestTest extends CiviEndToEndTestCase { |
49626e3d | 33 | protected $url; |
6769c30e | 34 | protected static $api_key; |
49626e3d CW |
35 | protected $session_id; |
36 | protected $nocms_contact_id; | |
38ba137b | 37 | protected $old_api_keys; |
61001223 | 38 | protected $adminContactId; |
49626e3d | 39 | |
4cbe18b8 EM |
40 | /** |
41 | * @param $apiResult | |
42 | * @param $cmpvar | |
43 | * @param string $prefix | |
44 | */ | |
66eec473 | 45 | protected function assertAPIErrorCode($apiResult, $cmpvar, $prefix = '') { |
49626e3d CW |
46 | if (!empty($prefix)) { |
47 | $prefix .= ': '; | |
48 | } | |
6769c30e TO |
49 | $this->assertEquals($cmpvar, $apiResult['is_error'], |
50 | $prefix . (empty($apiResult['error_message']) ? '' : $apiResult['error_message'])); | |
668891f2 | 51 | //$this->assertEquals($cmpvar, $apiResult['is_error'], $prefix . print_r($apiResult, TRUE)); |
49626e3d CW |
52 | } |
53 | ||
54 | protected function setUp() { | |
55 | parent::setUp(); | |
49626e3d | 56 | |
6769c30e TO |
57 | if (empty($GLOBALS['_CV']['CIVI_SITE_KEY'])) { |
58 | $this->markTestSkipped('Missing siteKey'); | |
eb87411f | 59 | } |
38ba137b TO |
60 | |
61 | $this->old_api_keys = array(); | |
49626e3d CW |
62 | } |
63 | ||
6769c30e TO |
64 | protected function getRestUrl() { |
65 | return CRM_Core_Resources::singleton() | |
66 | ->getUrl('civicrm', 'extern/rest.php'); | |
67 | } | |
68 | ||
49626e3d | 69 | protected function tearDown() { |
38ba137b TO |
70 | if (!empty($this->old_api_keys)) { |
71 | foreach ($this->old_api_keys as $cid => $apiKey) { | |
6769c30e | 72 | civicrm_api3('Contact', 'create', array( |
38ba137b TO |
73 | 'id' => $cid, |
74 | 'api_key' => $apiKey, | |
75 | )); | |
76 | } | |
77 | } | |
49626e3d | 78 | parent::tearDown(); |
016157f0 | 79 | if (isset($this->nocms_contact_id)) { |
49626e3d CW |
80 | $deleteParams = array( |
81 | "id" => $this->nocms_contact_id, | |
21dfd5f5 | 82 | "skip_undelete" => 1, |
49626e3d | 83 | ); |
6769c30e | 84 | $res = civicrm_api3("Contact", "delete", $deleteParams); |
49626e3d CW |
85 | unset($this->nocms_contact_id); |
86 | } | |
87 | } | |
88 | ||
3cabb1aa | 89 | /** |
668891f2 TO |
90 | * Build a list of test cases. Each test case defines a set of REST query |
91 | * parameters and an expected outcome for the REST request (eg is_error=>1 or is_error=>0). | |
92 | * | |
3cabb1aa TO |
93 | * @return array; each item is a list of parameters for testAPICalls |
94 | */ | |
00be9182 | 95 | public function apiTestCases() { |
3cabb1aa TO |
96 | $cases = array(); |
97 | ||
668891f2 TO |
98 | // entity,action: omit apiKey, valid entity+action |
99 | $cases[] = array( | |
39b959db SL |
100 | // query |
101 | array( | |
668891f2 TO |
102 | "entity" => "Contact", |
103 | "action" => "get", | |
6769c30e | 104 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
668891f2 TO |
105 | "json" => "1", |
106 | ), | |
39b959db SL |
107 | // is_error |
108 | 1, | |
668891f2 TO |
109 | ); |
110 | ||
3cabb1aa TO |
111 | // entity,action: valid apiKey, valid entity+action |
112 | $cases[] = array( | |
39b959db SL |
113 | // query |
114 | array( | |
3cabb1aa TO |
115 | "entity" => "Contact", |
116 | "action" => "get", | |
6769c30e | 117 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 118 | "json" => "1", |
6769c30e | 119 | "api_key" => self::getApiKey(), |
3cabb1aa | 120 | ), |
39b959db SL |
121 | // is_error |
122 | 0, | |
016157f0 | 123 | ); |
3cabb1aa TO |
124 | |
125 | // entity,action: bad apiKey, valid entity+action | |
126 | $cases[] = array( | |
39b959db SL |
127 | // query |
128 | array( | |
3cabb1aa TO |
129 | "entity" => "Contact", |
130 | "action" => "get", | |
6769c30e | 131 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 132 | "json" => "1", |
6769c30e | 133 | "api_key" => 'garbage_' . self::getApiKey(), |
3cabb1aa | 134 | ), |
39b959db SL |
135 | // is_error |
136 | 1, | |
3cabb1aa TO |
137 | ); |
138 | ||
139 | // entity,action: valid apiKey, invalid entity+action | |
140 | $cases[] = array( | |
39b959db SL |
141 | // query |
142 | array( | |
3cabb1aa TO |
143 | "entity" => "Contactses", |
144 | "action" => "get", | |
6769c30e | 145 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 146 | "json" => "1", |
6769c30e | 147 | "api_key" => self::getApiKey(), |
3cabb1aa | 148 | ), |
39b959db SL |
149 | // is_error |
150 | 1, | |
668891f2 TO |
151 | ); |
152 | ||
153 | // q=civicrm/entity/action: omit apiKey, valid entity+action | |
154 | $cases[] = array( | |
39b959db SL |
155 | // query |
156 | array( | |
668891f2 | 157 | "q" => "civicrm/contact/get", |
6769c30e | 158 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
668891f2 TO |
159 | "json" => "1", |
160 | ), | |
39b959db SL |
161 | // is_error |
162 | 1, | |
3cabb1aa TO |
163 | ); |
164 | ||
165 | // q=civicrm/entity/action: valid apiKey, valid entity+action | |
166 | $cases[] = array( | |
39b959db SL |
167 | // query |
168 | array( | |
3cabb1aa | 169 | "q" => "civicrm/contact/get", |
6769c30e | 170 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 171 | "json" => "1", |
6769c30e | 172 | "api_key" => self::getApiKey(), |
3cabb1aa | 173 | ), |
39b959db SL |
174 | // is_error |
175 | 0, | |
3cabb1aa TO |
176 | ); |
177 | ||
178 | // q=civicrm/entity/action: invalid apiKey, valid entity+action | |
179 | $cases[] = array( | |
39b959db SL |
180 | // query |
181 | array( | |
3cabb1aa | 182 | "q" => "civicrm/contact/get", |
6769c30e | 183 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 184 | "json" => "1", |
6769c30e | 185 | "api_key" => 'garbage_' . self::getApiKey(), |
3cabb1aa | 186 | ), |
39b959db SL |
187 | // is_error |
188 | 1, | |
3cabb1aa TO |
189 | ); |
190 | ||
191 | // q=civicrm/entity/action: valid apiKey, invalid entity+action | |
192 | $cases[] = array( | |
39b959db SL |
193 | // query |
194 | array( | |
3cabb1aa | 195 | "q" => "civicrm/contactses/get", |
6769c30e | 196 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
3cabb1aa | 197 | "json" => "1", |
6769c30e | 198 | "api_key" => self::getApiKey(), |
3cabb1aa | 199 | ), |
39b959db SL |
200 | // is_error |
201 | 1, | |
3cabb1aa TO |
202 | ); |
203 | ||
308e0075 | 204 | // q=civicrm/entity/action: valid apiKey, invalid entity+action |
cf739ea4 | 205 | // XXX Actually Ping is valid, no? |
308e0075 | 206 | $cases[] = array( |
39b959db SL |
207 | // query |
208 | array( | |
308e0075 | 209 | "q" => "civicrm/ping", |
6769c30e | 210 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
308e0075 | 211 | "json" => "1", |
6769c30e | 212 | "api_key" => self::getApiKey(), |
308e0075 | 213 | ), |
39b959db SL |
214 | // is_error |
215 | 0, | |
308e0075 TO |
216 | ); |
217 | ||
3cabb1aa | 218 | return $cases; |
49626e3d CW |
219 | } |
220 | ||
3cabb1aa TO |
221 | /** |
222 | * @dataProvider apiTestCases | |
1e1fdcf6 EM |
223 | * @param $query |
224 | * @param $is_error | |
3cabb1aa | 225 | */ |
00be9182 | 226 | public function testAPICalls($query, $is_error) { |
38ba137b TO |
227 | $this->updateAdminApiKey(); |
228 | ||
016157f0 | 229 | $client = CRM_Utils_HttpClient::singleton(); |
6769c30e | 230 | list($status, $data) = $client->post($this->getRestUrl(), $query); |
016157f0 TO |
231 | $this->assertEquals(CRM_Utils_HttpClient::STATUS_OK, $status); |
232 | $result = json_decode($data, TRUE); | |
d56bbdb3 | 233 | if ($result === NULL) { |
6769c30e TO |
234 | $msg = print_r(array( |
235 | 'restUrl' => $this->getRestUrl(), | |
236 | 'query' => $query, | |
237 | 'response data' => $data, | |
238 | ), TRUE); | |
d56bbdb3 TO |
239 | $this->assertNotNull($result, $msg); |
240 | } | |
3cabb1aa | 241 | $this->assertAPIErrorCode($result, $is_error); |
49626e3d CW |
242 | } |
243 | ||
0f051868 | 244 | /** |
eceb18cc | 245 | * Submit a request with an API key that exists but does not correspond to. |
0f051868 TO |
246 | * a real user. Submit in "?entity=X&action=X" notation |
247 | */ | |
00be9182 | 248 | public function testNotCMSUser_entityAction() { |
016157f0 | 249 | $client = CRM_Utils_HttpClient::singleton(); |
0f051868 | 250 | |
016157f0 TO |
251 | //Create contact with api_key |
252 | $test_key = "testing1234"; | |
253 | $contactParams = array( | |
254 | "api_key" => $test_key, | |
255 | "contact_type" => "Individual", | |
21dfd5f5 | 256 | "first_name" => "RestTester1", |
016157f0 | 257 | ); |
6769c30e | 258 | $contact = civicrm_api3("Contact", "create", $contactParams); |
016157f0 | 259 | $this->nocms_contact_id = $contact["id"]; |
49626e3d | 260 | |
83479334 | 261 | // The key associates with a real contact but not a real user |
016157f0 TO |
262 | $params = array( |
263 | "entity" => "Contact", | |
264 | "action" => "get", | |
6769c30e | 265 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
016157f0 | 266 | "json" => "1", |
21dfd5f5 | 267 | "api_key" => $test_key, |
0f051868 | 268 | ); |
6769c30e | 269 | list($status, $data) = $client->post($this->getRestUrl(), $params); |
0f051868 TO |
270 | $this->assertEquals(CRM_Utils_HttpClient::STATUS_OK, $status); |
271 | $result = json_decode($data, TRUE); | |
272 | $this->assertNotNull($result); | |
273 | $this->assertAPIErrorCode($result, 1); | |
274 | } | |
275 | ||
61001223 SL |
276 | /** |
277 | * Submit a request with an API key that exists but does not correspond to. | |
278 | * a real user. Submit in "?entity=X&action=X" notation | |
279 | */ | |
280 | public function testGetCorrectUserBack() { | |
281 | $this->updateAdminApiKey(); | |
282 | $client = CRM_Utils_HttpClient::singleton(); | |
283 | ||
284 | //Create contact with api_key | |
285 | // The key associates with a real contact but not a real user | |
286 | $params = array( | |
287 | "entity" => "Contact", | |
288 | "action" => "get", | |
289 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], | |
290 | "json" => "1", | |
291 | "api_key" => self::getApiKey(), | |
292 | "id" => "user_contact_id", | |
293 | ); | |
294 | list($status, $data) = $client->post($this->getRestUrl(), $params); | |
295 | $this->assertEquals(CRM_Utils_HttpClient::STATUS_OK, $status); | |
296 | $result = json_decode($data, TRUE); | |
297 | $this->assertNotNull($result); | |
298 | $this->assertEquals($result['id'], $this->adminContactId); | |
299 | } | |
300 | ||
0f051868 TO |
301 | /** |
302 | * Submit a request with an API key that exists but does not correspond to | |
303 | * a real user. Submit in "?q=civicrm/$entity/$action" notation | |
304 | */ | |
00be9182 | 305 | public function testNotCMSUser_q() { |
0f051868 TO |
306 | $client = CRM_Utils_HttpClient::singleton(); |
307 | ||
308 | //Create contact with api_key | |
309 | $test_key = "testing1234"; | |
310 | $contactParams = array( | |
311 | "api_key" => $test_key, | |
312 | "contact_type" => "Individual", | |
21dfd5f5 | 313 | "first_name" => "RestTester1", |
0f051868 | 314 | ); |
6769c30e | 315 | $contact = civicrm_api3("Contact", "create", $contactParams); |
0f051868 TO |
316 | $this->nocms_contact_id = $contact["id"]; |
317 | ||
83479334 | 318 | // The key associates with a real contact but not a real user |
0f051868 TO |
319 | $params = array( |
320 | "q" => "civicrm/contact/get", | |
6769c30e | 321 | "key" => $GLOBALS['_CV']['CIVI_SITE_KEY'], |
0f051868 | 322 | "json" => "1", |
21dfd5f5 | 323 | "api_key" => $test_key, |
016157f0 | 324 | ); |
6769c30e | 325 | list($status, $data) = $client->post($this->getRestUrl(), $params); |
016157f0 TO |
326 | $this->assertEquals(CRM_Utils_HttpClient::STATUS_OK, $status); |
327 | $result = json_decode($data, TRUE); | |
328 | $this->assertNotNull($result); | |
329 | $this->assertAPIErrorCode($result, 1); | |
49626e3d CW |
330 | } |
331 | ||
38ba137b | 332 | protected function updateAdminApiKey() { |
6769c30e | 333 | /** @var int $adminContactId */ |
61001223 | 334 | $this->adminContactId = civicrm_api3('contact', 'getvalue', array( |
6769c30e TO |
335 | 'id' => '@user:' . $GLOBALS['_CV']['ADMIN_USER'], |
336 | 'return' => 'id', | |
38ba137b TO |
337 | )); |
338 | ||
61001223 SL |
339 | $this->old_api_keys[$this->adminContactId] = CRM_Core_DAO::singleValueQuery('SELECT api_key FROM civicrm_contact WHERE id = %1', [ |
340 | 1 => [$this->adminContactId, 'Positive'], | |
420384a0 | 341 | ]); |
6769c30e TO |
342 | |
343 | //$this->old_admin_api_key = civicrm_api3('Contact', 'get', array( | |
344 | // 'id' => $adminContactId, | |
38ba137b TO |
345 | // 'return' => 'api_key', |
346 | //)); | |
347 | ||
6769c30e | 348 | civicrm_api3('Contact', 'create', array( |
61001223 | 349 | 'id' => $this->adminContactId, |
6769c30e | 350 | 'api_key' => self::getApiKey(), |
38ba137b TO |
351 | )); |
352 | } | |
353 | ||
6769c30e TO |
354 | protected static function getApiKey() { |
355 | if (empty(self::$api_key)) { | |
356 | self::$api_key = mt_rand() . mt_rand(); | |
357 | } | |
358 | return self::$api_key; | |
359 | } | |
360 | ||
49626e3d | 361 | } |