@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at upstream/main 371 lines 12 kB view raw
1<?php 2 3final class PhabricatorJIRAAuthProvider 4 extends PhabricatorOAuth1AuthProvider 5 implements DoorkeeperRemarkupURIInterface { 6 7 public function getProviderName() { 8 return pht('JIRA'); 9 } 10 11 public function getDescriptionForCreate() { 12 return pht('Configure JIRA OAuth. NOTE: Only supports JIRA 6.'); 13 } 14 15 public function getConfigurationHelp() { 16 return $this->getProviderConfigurationHelp(); 17 } 18 19 protected function getProviderConfigurationHelp() { 20 if ($this->isSetup()) { 21 return pht( 22 "**Step 1 of 2**: Provide the name and URI for your JIRA install.\n\n". 23 "In the next step, you will configure JIRA."); 24 } else { 25 $login_uri = PhabricatorEnv::getURI($this->getLoginURI()); 26 return pht( 27 "**Step 2 of 2**: In this step, you will configure JIRA.\n\n". 28 "**Create a JIRA Application**: Log into JIRA and go to ". 29 "**Administration**, then **Add-ons**, then **Application Links**. ". 30 "Click the button labeled **Add Application Link**, and use these ". 31 "settings to create an application:\n\n". 32 " - **Server URL**: `%s`\n". 33 " - Then, click **Next**. On the second page:\n". 34 " - **Application Name**: `%s`\n". 35 " - **Application Type**: `Generic Application`\n". 36 " - Then, click **Create**.\n\n". 37 "**Configure Your Application**: Find the application you just ". 38 "created in the table, and click the **Configure** link under ". 39 "**Actions**. Select **Incoming Authentication** and click the ". 40 "**OAuth** tab (it may be selected by default). Then, use these ". 41 "settings:\n\n". 42 " - **Consumer Key**: Set this to the \"Consumer Key\" value in the ". 43 "form above.\n". 44 " - **Consumer Name**: `%s`\n". 45 " - **Public Key**: Set this to the \"Public Key\" value in the ". 46 "form above.\n". 47 " - **Consumer Callback URL**: `%s`\n". 48 "Click **Save** in JIRA. Authentication should now be configured, ". 49 "and this provider should work correctly.", 50 PhabricatorEnv::getProductionURI('/'), 51 PlatformSymbols::getPlatformServerName(), 52 PlatformSymbols::getPlatformServerName(), 53 $login_uri); 54 } 55 } 56 57 protected function newOAuthAdapter() { 58 $config = $this->getProviderConfig(); 59 60 return id(new PhutilJIRAAuthAdapter()) 61 ->setAdapterDomain($config->getProviderDomain()) 62 ->setJIRABaseURI($config->getProperty(self::PROPERTY_JIRA_URI)) 63 ->setPrivateKey( 64 new PhutilOpaqueEnvelope( 65 $config->getProperty(self::PROPERTY_PRIVATE_KEY))); 66 } 67 68 protected function getLoginIcon() { 69 return 'Jira'; 70 } 71 72 private function isSetup() { 73 return !$this->getProviderConfig()->getID(); 74 } 75 76 const PROPERTY_JIRA_NAME = 'oauth1:jira:name'; 77 const PROPERTY_JIRA_URI = 'oauth1:jira:uri'; 78 const PROPERTY_PUBLIC_KEY = 'oauth1:jira:key:public'; 79 const PROPERTY_PRIVATE_KEY = 'oauth1:jira:key:private'; 80 const PROPERTY_REPORT_LINK = 'oauth1:jira:report:link'; 81 const PROPERTY_REPORT_COMMENT = 'oauth1:jira:report:comment'; 82 83 84 public function readFormValuesFromProvider() { 85 $config = $this->getProviderConfig(); 86 $uri = $config->getProperty(self::PROPERTY_JIRA_URI); 87 88 return array( 89 self::PROPERTY_JIRA_NAME => $this->getProviderDomain(), 90 self::PROPERTY_JIRA_URI => $uri, 91 ); 92 } 93 94 public function readFormValuesFromRequest(AphrontRequest $request) { 95 $is_setup = $this->isSetup(); 96 if ($is_setup) { 97 $name = $request->getStr(self::PROPERTY_JIRA_NAME); 98 } else { 99 $name = $this->getProviderDomain(); 100 } 101 102 return array( 103 self::PROPERTY_JIRA_NAME => $name, 104 self::PROPERTY_JIRA_URI => $request->getStr(self::PROPERTY_JIRA_URI), 105 self::PROPERTY_REPORT_LINK => 106 $request->getInt(self::PROPERTY_REPORT_LINK, 0), 107 self::PROPERTY_REPORT_COMMENT => 108 $request->getInt(self::PROPERTY_REPORT_COMMENT, 0), 109 ); 110 } 111 112 public function processEditForm( 113 AphrontRequest $request, 114 array $values) { 115 $errors = array(); 116 $issues = array(); 117 118 $is_setup = $this->isSetup(); 119 120 $key_name = self::PROPERTY_JIRA_NAME; 121 $key_uri = self::PROPERTY_JIRA_URI; 122 123 if (!strlen($values[$key_name])) { 124 $errors[] = pht('JIRA instance name is required.'); 125 $issues[$key_name] = pht('Required'); 126 } else if (!preg_match('/^[a-z0-9.]+\z/', $values[$key_name])) { 127 $errors[] = pht( 128 'JIRA instance name must contain only lowercase letters, digits, and '. 129 'period.'); 130 $issues[$key_name] = pht('Invalid'); 131 } 132 133 if (!strlen($values[$key_uri])) { 134 $errors[] = pht('JIRA base URI is required.'); 135 $issues[$key_uri] = pht('Required'); 136 } else { 137 $uri = new PhutilURI($values[$key_uri]); 138 if (!$uri->getProtocol()) { 139 $errors[] = pht( 140 'JIRA base URI should include protocol (like "https://").'); 141 $issues[$key_uri] = pht('Invalid'); 142 } 143 } 144 145 if (!$errors && $is_setup) { 146 $config = $this->getProviderConfig(); 147 148 $config->setProviderDomain($values[$key_name]); 149 150 $consumer_key = 'phjira.'.Filesystem::readRandomCharacters(16); 151 list($public, $private) = PhutilJIRAAuthAdapter::newJIRAKeypair(); 152 153 $config->setProperty(self::PROPERTY_PUBLIC_KEY, $public); 154 $config->setProperty(self::PROPERTY_PRIVATE_KEY, $private); 155 $config->setProperty(self::PROPERTY_CONSUMER_KEY, $consumer_key); 156 } 157 158 return array($errors, $issues, $values); 159 } 160 161 public function extendEditForm( 162 AphrontRequest $request, 163 AphrontFormView $form, 164 array $values, 165 array $issues) { 166 167 if (!function_exists('openssl_pkey_new')) { 168 // TODO: This could be a bit prettier. 169 throw new Exception( 170 pht( 171 "The PHP 'openssl' extension is not installed. You must install ". 172 "this extension in order to add a JIRA authentication provider, ". 173 "because JIRA OAuth requests use the RSA-SHA1 signing algorithm. ". 174 "Install the 'openssl' extension, restart everything, and try ". 175 "again.")); 176 } 177 178 $form->appendRemarkupInstructions( 179 pht( 180 'NOTE: This provider **only supports JIRA 6**. It will not work with '. 181 'JIRA 5 or earlier.')); 182 183 $is_setup = $this->isSetup(); 184 $viewer = $request->getViewer(); 185 186 $e_required = $request->isFormPost() ? null : true; 187 188 $v_name = $values[self::PROPERTY_JIRA_NAME]; 189 if ($is_setup) { 190 $e_name = idx($issues, self::PROPERTY_JIRA_NAME, $e_required); 191 } else { 192 $e_name = null; 193 } 194 195 $v_uri = $values[self::PROPERTY_JIRA_URI]; 196 $e_uri = idx($issues, self::PROPERTY_JIRA_URI, $e_required); 197 198 if ($is_setup) { 199 $form 200 ->appendRemarkupInstructions( 201 pht( 202 "**JIRA Instance Name**\n\n". 203 "Choose a permanent name for this instance of JIRA. This name is ". 204 "used internally to keep track of this particular instance of ". 205 "JIRA, in case the URL changes later.\n\n". 206 "Use lowercase letters, digits, and period. For example, ". 207 "`jira`, `jira.mycompany` or `jira.engineering` are reasonable ". 208 "names.")) 209 ->appendChild( 210 id(new AphrontFormTextControl()) 211 ->setLabel(pht('JIRA Instance Name')) 212 ->setValue($v_name) 213 ->setName(self::PROPERTY_JIRA_NAME) 214 ->setError($e_name)); 215 } else { 216 $form 217 ->appendChild( 218 id(new AphrontFormStaticControl()) 219 ->setLabel(pht('JIRA Instance Name')) 220 ->setValue($v_name)); 221 } 222 223 $form 224 ->appendChild( 225 id(new AphrontFormTextControl()) 226 ->setLabel(pht('JIRA Base URI')) 227 ->setValue($v_uri) 228 ->setName(self::PROPERTY_JIRA_URI) 229 ->setCaption( 230 pht( 231 'The URI where JIRA is installed. For example: %s', 232 phutil_tag('tt', array(), 'https://jira.mycompany.com/'))) 233 ->setError($e_uri)); 234 235 if (!$is_setup) { 236 $config = $this->getProviderConfig(); 237 238 239 $ckey = $config->getProperty(self::PROPERTY_CONSUMER_KEY); 240 $ckey = phutil_tag('tt', array(), $ckey); 241 242 $pkey = $config->getProperty(self::PROPERTY_PUBLIC_KEY); 243 $pkey = phutil_escape_html_newlines($pkey); 244 $pkey = phutil_tag('tt', array(), $pkey); 245 246 $form 247 ->appendRemarkupInstructions( 248 pht( 249 'NOTE: **To complete setup**, copy and paste these keys into JIRA '. 250 'according to the instructions below.')) 251 ->appendChild( 252 id(new AphrontFormStaticControl()) 253 ->setLabel(pht('Consumer Key')) 254 ->setValue($ckey)) 255 ->appendChild( 256 id(new AphrontFormStaticControl()) 257 ->setLabel(pht('Public Key')) 258 ->setValue($pkey)); 259 260 $form 261 ->appendRemarkupInstructions( 262 pht( 263 '= Integration Options = '."\n". 264 'Configure how to record Revisions on JIRA tasks.'."\n\n". 265 'Note you\'ll have to restart the daemons for this to take '. 266 'effect.')) 267 ->appendChild( 268 id(new AphrontFormCheckboxControl()) 269 ->addCheckbox( 270 self::PROPERTY_REPORT_LINK, 271 1, 272 new PHUIRemarkupView( 273 $viewer, 274 pht( 275 'Create **Issue Link** to the Revision, as an "implemented '. 276 'in" relationship.')), 277 $this->shouldCreateJIRALink())) 278 ->appendChild( 279 id(new AphrontFormCheckboxControl()) 280 ->addCheckbox( 281 self::PROPERTY_REPORT_COMMENT, 282 1, 283 new PHUIRemarkupView( 284 $viewer, 285 pht( 286 '**Post a comment** in the JIRA task.')), 287 $this->shouldCreateJIRAComment())); 288 } 289 290 } 291 292 /** 293 * JIRA uses a setup step to generate public/private keys. 294 */ 295 public function hasSetupStep() { 296 return true; 297 } 298 299 public static function getJIRAProvider() { 300 $providers = self::getAllEnabledProviders(); 301 302 foreach ($providers as $provider) { 303 if ($provider instanceof PhabricatorJIRAAuthProvider) { 304 return $provider; 305 } 306 } 307 308 return null; 309 } 310 311 public function newJIRAFuture( 312 PhabricatorExternalAccount $account, 313 $path, 314 $method, 315 $params = array()) { 316 317 $adapter = clone $this->getAdapter(); 318 $adapter->setToken($account->getProperty('oauth1.token')); 319 $adapter->setTokenSecret($account->getProperty('oauth1.token.secret')); 320 321 return $adapter->newJIRAFuture($path, $method, $params); 322 } 323 324 public function shouldCreateJIRALink() { 325 $config = $this->getProviderConfig(); 326 return $config->getProperty(self::PROPERTY_REPORT_LINK, true); 327 } 328 329 public function shouldCreateJIRAComment() { 330 $config = $this->getProviderConfig(); 331 return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true); 332 } 333 334/* -( DoorkeeperRemarkupURIInterface )------------------------------------- */ 335 336 public function getDoorkeeperURIRef(PhutilURI $uri) { 337 $uri_string = phutil_string_cast($uri); 338 339 $pattern = '((https?://\S+?)/browse/([A-Z][A-Z0-9]*-[1-9]\d*))'; 340 $matches = null; 341 if (!preg_match($pattern, $uri_string, $matches)) { 342 return null; 343 } 344 345 if (strlen($uri->getFragment())) { 346 return null; 347 } 348 349 if ($uri->getQueryParamsAsPairList()) { 350 return null; 351 } 352 353 $domain = $matches[1]; 354 $issue = $matches[2]; 355 356 $config = $this->getProviderConfig(); 357 $base_uri = $config->getProperty(self::PROPERTY_JIRA_URI); 358 359 if ($domain !== rtrim($base_uri, '/')) { 360 return null; 361 } 362 363 return id(new DoorkeeperURIRef()) 364 ->setURI($uri) 365 ->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA) 366 ->setApplicationDomain($this->getProviderDomain()) 367 ->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE) 368 ->setObjectID($issue); 369 } 370 371}