CRM-16173 - Add Cxn.register API. Add extern/cxn.php endpoint.
authorTim Otten <totten@civicrm.org>
Tue, 24 Mar 2015 23:49:54 +0000 (16:49 -0700)
committerTim Otten <totten@civicrm.org>
Tue, 14 Jul 2015 04:00:06 +0000 (21:00 -0700)
CRM/Cxn/BAO/Cxn.php [new file with mode: 0644]
CRM/Cxn/CiviCxnStore.php [new file with mode: 0644]
api/v3/Cxn.php [new file with mode: 0644]
extern/cxn.php [new file with mode: 0644]

diff --git a/CRM/Cxn/BAO/Cxn.php b/CRM/Cxn/BAO/Cxn.php
new file mode 100644 (file)
index 0000000..bfc7ce3
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6                                                |
+ +--------------------------------------------------------------------+
+ | Copyright (C) 2011 Marty Wright                                    |
+ | Licensed to CiviCRM under the Academic Free License version 3.0.   |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2014
+ * $Id$
+ *
+ */
+
+/**
+ * This class helps to manage connections to third-party apps.
+ */
+class CRM_Cxn_BAO_Cxn extends CRM_Cxn_DAO_Cxn {
+  public static function getSiteCallbackUrl() {
+    $config = CRM_Core_Config::singleton();
+    if (preg_match('/^(http|https):/', $config->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 (file)
index 0000000..8503fa2
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+
+class CRM_Cxn_CiviCxnStore implements Civi\Cxn\Rpc\CxnStore\CxnStoreInterface {
+
+  protected $cxns = array();
+
+  /**
+   * @inheritDoc
+   */
+  public function getAll() {
+    if (!$this->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 (file)
index 0000000..2d805d8
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2014                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+
+/**
+ * @param array $params
+ *   Array with keys:
+ *   - appMeta: the application's metadata.
+ * @return array
+ */
+function civicrm_api3_cxn_register($params) {
+  if (empty($params['appMeta']) && !empty($params['appMetaUrl'])) {
+    if (defined('CIVICRM_CXN_VERIFY') && !CIVICRM_CXN_VERIFY) {
+      list ($status, $json) = CRM_Utils_HttpClient::singleton()->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 (file)
index 0000000..cc40d1a
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6                                                |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2014                                |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM.                                    |
+ |                                                                    |
+ | CiviCRM is free software; you can copy, modify, and distribute it  |
+ | under the terms of the GNU Affero General Public License           |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
+ |                                                                    |
+ | CiviCRM is distributed in the hope that it will be useful, but     |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of         |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
+ | See the GNU Affero General Public License for more details.        |
+ |                                                                    |
+ | You should have received a copy of the GNU Affero General Public   |
+ | License and the CiviCRM Licensing Exception along                  |
+ | with this program; if not, contact CiviCRM LLC                     |
+ | at info[AT]civicrm[DOT]org. If you have questions about the        |
+ | GNU Affero General Public License or the licensing of CiviCRM,     |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
+ +--------------------------------------------------------------------+
+ */
+
+require_once '../civicrm.config.php';
+$config = CRM_Core_Config::singleton();
+
+CRM_Utils_System::loadBootStrap(array(), FALSE);
+
+$apiServer = new \Civi\Cxn\Rpc\ApiServer(new CRM_Cxn_CiviCxnStore());
+$apiServer->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'));