$this->savedSearch = ['api_entity' => $entityName];
}
$this->loadSavedSearch();
+ // Pass-through this parameter
+ $this->_apiParams['checkPermissions'] = $this->savedSearch['api_params']['checkPermissions'] = $this->getCheckPermissions();
// Render mode: fetch by id
if ($this->ids) {
$this->_apiParams['where'][] = [$idField, 'IN', $this->ids];
else {
$this->_apiParams['select'] = array_unique(array_merge($this->_apiParams['select'], $select));
}
- $this->_apiParams['checkPermissions'] = $this->getCheckPermissions();
$this->applyFilters();
$apiResult = civicrm_api4($entityName, 'get', $this->_apiParams);
$rawResults = array_slice((array) $apiResult, 0, $resultsPerPage);
// Proceed only if permissions are being enforced.'
// Anonymous users in permission-bypass mode should not be allowed to set arbitrary filters.
if ($this->getCheckPermissions()) {
- $field = $this->getField($fieldName);
- try {
- civicrm_api4($field['entity'], 'getFields', [
- 'action' => 'get',
- 'where' => [['name', '=', $fieldName]],
- ])->single();
- return TRUE;
- }
- catch (\CRM_Core_Exception $e) {
- return FALSE;
- }
+ // This function checks field permissions
+ return (bool) $this->getField($fieldName);
}
return FALSE;
}
+ /**
+ * @return array
+ */
+ public function getPermissions() {
+ // Permissions for this action are checked internally
+ return [];
+ }
+
}
*/
class AutocompleteTest extends Api4TestBase implements HookInterface, TransactionalInterface {
+ /**
+ * @var callable
+ */
+ private $hookCallback;
+
/**
* Listens for civi.api4.entityTypes event to manually add this nonstandard entity
*
$e->entities['MockBasicEntity'] = MockBasicEntity::getInfo();
}
+ public function on_civi_api_prepare(\Civi\API\Event\PrepareEvent $event) {
+ $apiRequest = $event->getApiRequest();
+ if ($this->hookCallback && is_object($apiRequest) && is_a($apiRequest, 'Civi\Api4\Generic\AutocompleteAction')) {
+ ($this->hookCallback)($apiRequest);
+ }
+ }
+
public function setUp(): void {
+ $this->hookCallback = NULL;
// Ensure MockBasicEntity gets added via above listener
\Civi::cache('metadata')->clear();
MockBasicEntity::delete(FALSE)->addWhere('identifier', '>', 0)->execute();
$this->assertEquals('fa-star', $result[2]['icon']);
}
+ public function testAutocompleteValidation() {
+ $lastName = uniqid(__FUNCTION__);
+ $sampleData = [
+ [
+ 'first_name' => 'One',
+ 'api_key' => 'secret123',
+ ],
+ [
+ 'first_name' => 'Two',
+ 'api_key' => 'secret456',
+ ],
+ [
+ 'first_name' => 'Three',
+ 'api_key' => 'secret789',
+ ],
+ ];
+ $records = $this->saveTestRecords('Contact', [
+ 'records' => $sampleData,
+ 'defaults' => ['last_name' => $lastName],
+ ]);
+
+ $this->createLoggedInUser();
+
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = [
+ 'administer CiviCRM',
+ 'view all contacts',
+ ];
+
+ // Admin can apply the api_key filter
+ $result = Contact::autocomplete()
+ ->setInput($lastName)
+ ->setClientFilters(['api_key' => 'secret789'])
+ ->execute();
+ $this->assertCount(1, $result);
+
+ \CRM_Core_Config::singleton()->userPermissionClass->permissions = [
+ 'access CiviCRM',
+ 'view all contacts',
+ ];
+
+ // Non-admin cannot apply filter
+ $result = Contact::autocomplete()
+ ->setInput($lastName)
+ ->setClientFilters(['api_key' => 'secret789'])
+ ->execute();
+ $this->assertCount(3, $result);
+
+ // Cannot apply filter even with permissions disabled
+ $result = Contact::autocomplete(FALSE)
+ ->setInput($lastName)
+ ->setClientFilters(['api_key' => 'secret789'])
+ ->execute();
+ $this->assertCount(3, $result);
+
+ // Assert that the end-user is not allowed to inject arbitrary savedSearch params
+ $msg = '';
+ try {
+ $result = Contact::autocomplete()
+ ->setInput($lastName)
+ ->setSavedSearch([
+ 'api_entity' => 'Contact',
+ 'api_params' => [],
+ ])
+ ->execute();
+ }
+ catch (\CRM_Core_Exception $e) {
+ $msg = $e->getMessage();
+ }
+ $this->assertEquals('Parameter "savedSearch" is not of the correct type. Expecting string.', $msg);
+
+ // With hook callback, permissions can be overridden by injecting a trusted filter
+ $this->hookCallback = function(\Civi\Api4\Generic\AutocompleteAction $action) {
+ $action->addFilter('api_key', 'secret456');
+ $action->setCheckPermissions(FALSE);
+ };
+
+ $result = Contact::autocomplete()
+ ->setInput($lastName)
+ ->execute();
+ $this->assertCount(1, $result);
+ }
+
}