Merge pull request #23939 from civicrm/5.51
[civicrm-core.git] / CRM / Core / BAO / CustomValue.php
index ebf1f75aa09a7897e8e9fedfef33c479966a3a47..c88c62bfebd78922f3335cc67ff9d3ce8f073856 100644 (file)
@@ -221,4 +221,56 @@ class CRM_Core_BAO_CustomValue extends CRM_Core_DAO {
     return $clauses;
   }
 
+  /**
+   * Special checkAccess function for multi-record custom pseudo-entities
+   *
+   * @param string $entityName
+   *   Ex: 'Contact' or 'Custom_Foobar'
+   * @param string $action
+   * @param array $record
+   * @param int $userID
+   *   Contact ID of the active user (whose access we must check). 0 for anonymous.
+   * @return bool
+   *   TRUE if granted. FALSE if prohibited. NULL if indeterminate.
+   */
+  public static function _checkAccess(string $entityName, string $action, array $record, int $userID): ?bool {
+    // This check implements two rules: you must have access to the specific custom-data-group - and to the underlying record (e.g. Contact).
+
+    $groupName = substr($entityName, 0, 7) === 'Custom_' ? substr($entityName, 7) : NULL;
+    $extends = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $groupName, 'extends', 'name');
+    $id = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $groupName, 'id', 'name');
+    if (!$groupName) {
+      // $groupName is required but the function signature has to match the parent.
+      throw new CRM_Core_Exception('Missing required group-name in CustomValue::checkAccess');
+    }
+
+    if (empty($extends) || empty($id)) {
+      throw new CRM_Core_Exception('Received invalid group-name in CustomValue::checkAccess');
+    }
+
+    $actionType = $action === 'get' ? CRM_Core_Permission::VIEW : CRM_Core_Permission::EDIT;
+    if (!\CRM_Core_BAO_CustomGroup::checkGroupAccess($id, $actionType, $userID)) {
+      return FALSE;
+    }
+
+    $eid = $record['entity_id'] ?? NULL;
+    if (!$eid) {
+      $tableName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $groupName, 'table_name', 'name');
+      $eid = CRM_Core_DAO::singleValueQuery("SELECT entity_id FROM `$tableName` WHERE id = " . (int) $record['id']);
+    }
+
+    // Do we have access to the target record?
+    if ($extends === 'Contact' || in_array($extends, CRM_Contact_BAO_ContactType::basicTypes(TRUE), TRUE)) {
+      return \Civi\Api4\Utils\CoreUtil::checkAccessDelegated('Contact', 'update', ['id' => $eid], $userID);
+    }
+    elseif (\Civi\Api4\Utils\CoreUtil::getApiClass($extends)) {
+      // For most entities (Activity, Relationship, Contribution, ad nauseum), we acn just use an eponymous API.
+      return \Civi\Api4\Utils\CoreUtil::checkAccessDelegated($extends, 'update', ['id' => $eid], $userID);
+    }
+    else {
+      // Do you need to add a special case for some oddball custom-group type?
+      throw new CRM_Core_Exception("Cannot assess delegated permissions for group {$groupName}.");
+    }
+  }
+
 }