From: Tim Otten Date: Tue, 24 Mar 2015 23:49:54 +0000 (-0700) Subject: CRM-16173 - Add Cxn.register API. Add extern/cxn.php endpoint. X-Git-Url: https://vcs.fsf.org/?a=commitdiff_plain;h=5d5d3b35f73c0d71e6dacb50dc35f92a047c78bc;p=civicrm-core.git CRM-16173 - Add Cxn.register API. Add extern/cxn.php endpoint. --- diff --git a/CRM/Cxn/BAO/Cxn.php b/CRM/Cxn/BAO/Cxn.php new file mode 100644 index 0000000000..bfc7ce364d --- /dev/null +++ b/CRM/Cxn/BAO/Cxn.php @@ -0,0 +1,59 @@ +resourceBase)) { + $civiUrl = $config->resourceBase; + } + else { + $civiUrl = rtrim(CRM_Utils_System::baseURL(), '/') . '/' . ltrim($config->resourceBase, '/'); + } + return rtrim($civiUrl, '/') . '/extern/cxn.php'; + } + + public static function updateAppMeta($appMeta) { + \Civi\Cxn\Rpc\AppMeta::validate($appMeta); + CRM_Core_DAO::executeQuery('UPDATE civicrm_cxn SET app_meta = %1 WHERE app_id = %2', array( + 1 => array(json_encode($appMeta), 'String'), + 2 => array($appMeta['appId'], 'String'), + )); + } +} diff --git a/CRM/Cxn/CiviCxnStore.php b/CRM/Cxn/CiviCxnStore.php new file mode 100644 index 0000000000..8503fa24ab --- /dev/null +++ b/CRM/Cxn/CiviCxnStore.php @@ -0,0 +1,119 @@ +cxns) { + $this->cxns = array(); + $dao = new CRM_Cxn_DAO_Cxn(); + $dao->find(); + while ($dao->fetch()) { + $cxn = $this->convertDaoToCxn($dao); + $this->cxns[$cxn['cxnId']] = $cxn; + } + } + return $this->cxns; + } + + /** + * @inheritDoc + */ + public function getByCxnId($cxnId) { + if (isset($this->cxns[$cxnId])) { + return $this->cxns[$cxnId]; + } + $dao = new CRM_Cxn_DAO_Cxn(); + $dao->cxn_id = $cxnId; + if ($dao->find(TRUE)) { + $this->cxns[$cxnId] = $this->convertDaoToCxn($dao); + return $this->cxns[$cxnId]; + } + else { + return NULL; + } + } + + /** + * @inheritDoc + */ + public function getByAppId($appId) { + $dao = new CRM_Cxn_DAO_Cxn(); + $dao->app_id = $appId; + if ($dao->find(TRUE)) { + $this->cxns[$dao->cxn_id] = $this->convertDaoToCxn($dao); + return $this->cxns[$dao->cxn_id]; + } + else { + return NULL; + } + } + + /** + * @inheritDoc + */ + public function add($cxn) { + $dao = new CRM_Cxn_DAO_Cxn(); + $dao->cxn_id = $cxn['cxnId']; + $dao->find(TRUE); + $this->convertCxnToDao($cxn, $dao); + $dao->save(); + + $sql = ' + UPDATE civicrm_cxn SET created_date = modified_date + WHERE created_date IS NULL + AND cxn_guid = %1 + '; + CRM_Core_DAO::executeQuery($sql, array( + 1 => array($cxn['cxnId'], 'String'), + )); + + $this->cxns[$cxn['cxnId']] = $cxn; + } + + /** + * @inheritDoc + */ + public function remove($cxnId) { + CRM_Core_DAO::executeQuery('DELETE FROM civicrm_cxn WHERE cxn_id = %1', array( + 1 => array($cxnId, 'String'), + )); + unset($this->cxns[$cxnId]); + } + + /** + * @param CRM_Cxn_DAO_Cxn $dao + * @return array + * Array-encoded connection details. + */ + protected function convertDaoToCxn($dao) { + $appMeta = json_decode($dao->app_meta, TRUE); + return array( + 'cxnId' => $dao->cxn_id, + 'secret' => $dao->secret, + 'appId' => $dao->app_id, + 'appUrl' => $appMeta['appUrl'], + 'siteUrl' => CRM_Cxn_BAO_Cxn::getSiteCallbackUrl(), + 'perm' => json_decode($dao->perm, TRUE), + ); + } + + /** + * @param array $cxn + * Array-encoded connection details. + * @param CRM_Cxn_DAO_Cxn $dao + */ + protected function convertCxnToDao($cxn, $dao) { + $dao->cxn_id = $cxn['cxnId']; + $dao->secret = $cxn['secret']; + $dao->app_id = $cxn['appId']; + $dao->perm = json_encode($cxn['perm']); + + // Note: we don't save siteUrl because it's more correct to regenerate on-demand. + // Note: we don't save appUrl, but other processes will update appMeta. + } +} diff --git a/api/v3/Cxn.php b/api/v3/Cxn.php new file mode 100644 index 0000000000..2d805d87bc --- /dev/null +++ b/api/v3/Cxn.php @@ -0,0 +1,79 @@ +get($params['appMetaUrl']); + if (CRM_Utils_HttpClient::STATUS_OK != $status) { + throw new API_Exception("Failed to download appMeta."); + } + $params['appMeta'] = json_decode($json, TRUE); + } + else { + // Note: The metadata includes a cert, but the details aren't signed. + // This is very useful in testing/development. In ordinary usage, we + // rely on a general signature for the full batch of all metadata. + throw new API_Exception('This site is configured to only use verified metadata.'); + } + } + + if (empty($params['appMeta']) || !is_array($params['appMeta'])) { + throw new API_Exception("Missing expected parameter: appMeta (array)"); + } + \Civi\Cxn\Rpc\AppMeta::validate($params['appMeta']); + + // FIXME Move cxnStore and client into Container. + $cxnStore = new CRM_Cxn_CiviCxnStore(); + try { + $client = new \Civi\Cxn\Rpc\RegistrationClient(NULL, $cxnStore, CRM_Cxn_BAO_Cxn::getSiteCallbackUrl()); + $client->setLog(new CRM_Utils_SystemLogger()); + list($cxnId, $isOk) = $client->register($params['appMeta']); + CRM_Cxn_BAO_Cxn::updateAppMeta($params['appMeta']); + } + catch (Exception $e) { + CRM_Cxn_BAO_Cxn::updateAppMeta($params['appMeta']); + throw $e; + } + + if ($isOk) { + $result = array( + 'cxnId' => $cxnId, + ); + return civicrm_api3_create_success(); + } + else { + return civicrm_api3_create_error('Connection failed'); + } +} diff --git a/extern/cxn.php b/extern/cxn.php new file mode 100644 index 0000000000..cc40d1acac --- /dev/null +++ b/extern/cxn.php @@ -0,0 +1,53 @@ +setLog(new CRM_Utils_SystemLogger()); +$apiServer->setRouter(function ($cxn, $entity, $action, $params) { + require_once 'api/v3/utils.php'; + + // Note: cxnId is authenticated before router is called. + $dao = new CRM_Cxn_DAO_Cxn(); + $dao->cxn_id = $cxn['cxnId']; + if (empty($cxn['cxnId']) || !$dao->find(TRUE) || !$dao->cxn_id) { + return civicrm_api3_create_error('Failed to lookup connection authorizations.'); + } + if (!$dao->is_active) { + return civicrm_api3_create_error('Connection is inactive'); + } + + // FIXME: apply $dao->perm + + return civicrm_api($entity, $action, $params); + +}); +$apiServer->handleAndRespond(file_get_contents('php://input'));