[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/conduit/call/ -> ConduitCall.php (source)

   1  <?php
   2  
   3  /**
   4   * Run a conduit method in-process, without requiring HTTP requests. Usage:
   5   *
   6   *   $call = new ConduitCall('method.name', array('param' => 'value'));
   7   *   $call->setUser($user);
   8   *   $result = $call->execute();
   9   *
  10   */
  11  final class ConduitCall {
  12  
  13    private $method;
  14    private $request;
  15    private $user;
  16    private $servers;
  17    private $forceLocal;
  18  
  19    public function __construct($method, array $params) {
  20      $this->method     = $method;
  21      $this->handler    = $this->buildMethodHandler($method);
  22      $this->servers    = PhabricatorEnv::getEnvConfig('conduit.servers');
  23      $this->forceLocal = false;
  24  
  25      $invalid_params = array_diff_key(
  26        $params,
  27        $this->handler->defineParamTypes());
  28      if ($invalid_params) {
  29        throw new ConduitException(
  30          "Method '{$method}' doesn't define these parameters: '".
  31          implode("', '", array_keys($invalid_params))."'.");
  32      }
  33  
  34      if ($this->servers) {
  35        $current_host = AphrontRequest::getHTTPHeader('HOST');
  36        foreach ($this->servers as $server) {
  37          if ($current_host === id(new PhutilURI($server))->getDomain()) {
  38            $this->forceLocal = true;
  39            break;
  40          }
  41        }
  42      }
  43  
  44      $this->request = new ConduitAPIRequest($params);
  45    }
  46  
  47    public function setUser(PhabricatorUser $user) {
  48      $this->user = $user;
  49      return $this;
  50    }
  51  
  52    public function getUser() {
  53      return $this->user;
  54    }
  55  
  56    public function setForceLocal($force_local) {
  57      $this->forceLocal = $force_local;
  58      return $this;
  59    }
  60  
  61    public function shouldForceLocal() {
  62      return $this->forceLocal;
  63    }
  64  
  65    public function shouldRequireAuthentication() {
  66      return $this->handler->shouldRequireAuthentication();
  67    }
  68  
  69    public function shouldAllowUnguardedWrites() {
  70      return $this->handler->shouldAllowUnguardedWrites();
  71    }
  72  
  73    public function getRequiredScope() {
  74      return $this->handler->getRequiredScope();
  75    }
  76  
  77    public function getErrorDescription($code) {
  78      return $this->handler->getErrorDescription($code);
  79    }
  80  
  81    public function execute() {
  82      $profiler = PhutilServiceProfiler::getInstance();
  83      $call_id = $profiler->beginServiceCall(
  84        array(
  85          'type' => 'conduit',
  86          'method' => $this->method,
  87        ));
  88  
  89      try {
  90        $result = $this->executeMethod();
  91      } catch (Exception $ex) {
  92        $profiler->endServiceCall($call_id, array());
  93        throw $ex;
  94      }
  95  
  96      $profiler->endServiceCall($call_id, array());
  97      return $result;
  98    }
  99  
 100    private function executeMethod() {
 101      $user = $this->getUser();
 102      if (!$user) {
 103        $user = new PhabricatorUser();
 104      }
 105  
 106      $this->request->setUser($user);
 107  
 108      if (!$this->shouldRequireAuthentication()) {
 109        // No auth requirement here.
 110      } else {
 111  
 112        $allow_public = $this->handler->shouldAllowPublic() &&
 113                        PhabricatorEnv::getEnvConfig('policy.allow-public');
 114        if (!$allow_public) {
 115          if (!$user->isLoggedIn() && !$user->isOmnipotent()) {
 116            // TODO: As per below, this should get centralized and cleaned up.
 117            throw new ConduitException('ERR-INVALID-AUTH');
 118          }
 119        }
 120  
 121        // TODO: This would be slightly cleaner by just using a Query, but the
 122        // Conduit auth workflow requires the Call and User be built separately.
 123        // Just do it this way for the moment.
 124        $application = $this->handler->getApplication();
 125        if ($application) {
 126          $can_view = PhabricatorPolicyFilter::hasCapability(
 127            $user,
 128            $application,
 129            PhabricatorPolicyCapability::CAN_VIEW);
 130  
 131          if (!$can_view) {
 132            throw new ConduitException(
 133              pht(
 134                'You do not have access to the application which provides this '.
 135                'API method.'));
 136          }
 137        }
 138      }
 139  
 140      if (!$this->shouldForceLocal() && $this->servers) {
 141        $server = $this->pickRandomServer($this->servers);
 142        $client = new ConduitClient($server);
 143        $params = $this->request->getAllParameters();
 144  
 145        $params['__conduit__']['isProxied'] = true;
 146  
 147        if ($this->handler->shouldRequireAuthentication()) {
 148          $client->callMethodSynchronous(
 149          'conduit.connect',
 150          array(
 151               'client'            => 'PhabricatorConduit',
 152               'clientVersion'     => '1.0',
 153               'user'              => $this->getUser()->getUserName(),
 154               'certificate'       => $this->getUser()->getConduitCertificate(),
 155               '__conduit__'       => $params['__conduit__'],
 156          ));
 157        }
 158  
 159        return $client->callMethodSynchronous(
 160          $this->method,
 161          $params);
 162      } else {
 163        return $this->handler->executeMethod($this->request);
 164      }
 165    }
 166  
 167    protected function pickRandomServer($servers) {
 168      return $servers[array_rand($servers)];
 169    }
 170  
 171    protected function buildMethodHandler($method_name) {
 172      $method = ConduitAPIMethod::getConduitMethod($method_name);
 173  
 174      if (!$method) {
 175        throw new ConduitMethodDoesNotExistException($method_name);
 176      }
 177  
 178      $application = $method->getApplication();
 179      if ($application && !$application->isInstalled()) {
 180        $app_name = $application->getName();
 181        throw new ConduitApplicationNotInstalledException($method, $app_name);
 182      }
 183  
 184      return $method;
 185    }
 186  
 187  
 188  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1