[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |