[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 abstract class PhabricatorAuthProvider { 4 5 private $providerConfig; 6 7 public function attachProviderConfig(PhabricatorAuthProviderConfig $config) { 8 $this->providerConfig = $config; 9 return $this; 10 } 11 12 public function hasProviderConfig() { 13 return (bool)$this->providerConfig; 14 } 15 16 public function getProviderConfig() { 17 if ($this->providerConfig === null) { 18 throw new Exception( 19 'Call attachProviderConfig() before getProviderConfig()!'); 20 } 21 return $this->providerConfig; 22 } 23 24 public function getConfigurationHelp() { 25 return null; 26 } 27 28 public function getDefaultProviderConfig() { 29 return id(new PhabricatorAuthProviderConfig()) 30 ->setProviderClass(get_class($this)) 31 ->setIsEnabled(1) 32 ->setShouldAllowLogin(1) 33 ->setShouldAllowRegistration(1) 34 ->setShouldAllowLink(1) 35 ->setShouldAllowUnlink(1); 36 } 37 38 public function getNameForCreate() { 39 return $this->getProviderName(); 40 } 41 42 public function getDescriptionForCreate() { 43 return null; 44 } 45 46 public function getProviderKey() { 47 return $this->getAdapter()->getAdapterKey(); 48 } 49 50 public function getProviderType() { 51 return $this->getAdapter()->getAdapterType(); 52 } 53 54 public function getProviderDomain() { 55 return $this->getAdapter()->getAdapterDomain(); 56 } 57 58 public static function getAllBaseProviders() { 59 static $providers; 60 61 if ($providers === null) { 62 $objects = id(new PhutilSymbolLoader()) 63 ->setAncestorClass(__CLASS__) 64 ->loadObjects(); 65 $providers = $objects; 66 } 67 68 return $providers; 69 } 70 71 public static function getAllProviders() { 72 static $providers; 73 74 if ($providers === null) { 75 $objects = self::getAllBaseProviders(); 76 77 $configs = id(new PhabricatorAuthProviderConfigQuery()) 78 ->setViewer(PhabricatorUser::getOmnipotentUser()) 79 ->execute(); 80 81 $providers = array(); 82 foreach ($configs as $config) { 83 if (!isset($objects[$config->getProviderClass()])) { 84 // This configuration is for a provider which is not installed. 85 continue; 86 } 87 88 $object = clone $objects[$config->getProviderClass()]; 89 $object->attachProviderConfig($config); 90 91 $key = $object->getProviderKey(); 92 if (isset($providers[$key])) { 93 throw new Exception( 94 pht( 95 "Two authentication providers use the same provider key ". 96 "('%s'). Each provider must be identified by a unique key.", 97 $key)); 98 } 99 $providers[$key] = $object; 100 } 101 } 102 103 return $providers; 104 } 105 106 public static function getAllEnabledProviders() { 107 $providers = self::getAllProviders(); 108 foreach ($providers as $key => $provider) { 109 if (!$provider->isEnabled()) { 110 unset($providers[$key]); 111 } 112 } 113 return $providers; 114 } 115 116 public static function getEnabledProviderByKey($provider_key) { 117 return idx(self::getAllEnabledProviders(), $provider_key); 118 } 119 120 abstract public function getProviderName(); 121 abstract public function getAdapter(); 122 123 public function isEnabled() { 124 return $this->getProviderConfig()->getIsEnabled(); 125 } 126 127 public function shouldAllowLogin() { 128 return $this->getProviderConfig()->getShouldAllowLogin(); 129 } 130 131 public function shouldAllowRegistration() { 132 return $this->getProviderConfig()->getShouldAllowRegistration(); 133 } 134 135 public function shouldAllowAccountLink() { 136 return $this->getProviderConfig()->getShouldAllowLink(); 137 } 138 139 public function shouldAllowAccountUnlink() { 140 return $this->getProviderConfig()->getShouldAllowUnlink(); 141 } 142 143 public function shouldTrustEmails() { 144 return $this->shouldAllowEmailTrustConfiguration() && 145 $this->getProviderConfig()->getShouldTrustEmails(); 146 } 147 148 /** 149 * Should we allow the adapter to be marked as "trusted". This is true for 150 * all adapters except those that allow the user to type in emails (see 151 * @{class:PhabricatorPasswordAuthProvider}). 152 */ 153 public function shouldAllowEmailTrustConfiguration() { 154 return true; 155 } 156 157 public function buildLoginForm(PhabricatorAuthStartController $controller) { 158 return $this->renderLoginForm($controller->getRequest(), $mode = 'start'); 159 } 160 161 abstract public function processLoginRequest( 162 PhabricatorAuthLoginController $controller); 163 164 public function buildLinkForm(PhabricatorAuthLinkController $controller) { 165 return $this->renderLoginForm($controller->getRequest(), $mode = 'link'); 166 } 167 168 public function shouldAllowAccountRefresh() { 169 return true; 170 } 171 172 public function buildRefreshForm( 173 PhabricatorAuthLinkController $controller) { 174 return $this->renderLoginForm($controller->getRequest(), $mode = 'refresh'); 175 } 176 177 protected function renderLoginForm(AphrontRequest $request, $mode) { 178 throw new PhutilMethodNotImplementedException(); 179 } 180 181 public function createProviders() { 182 return array($this); 183 } 184 185 protected function willSaveAccount(PhabricatorExternalAccount $account) { 186 return; 187 } 188 189 public function willRegisterAccount(PhabricatorExternalAccount $account) { 190 return; 191 } 192 193 protected function loadOrCreateAccount($account_id) { 194 if (!strlen($account_id)) { 195 throw new Exception('loadOrCreateAccount(...): empty account ID!'); 196 } 197 198 $adapter = $this->getAdapter(); 199 $adapter_class = get_class($adapter); 200 201 if (!strlen($adapter->getAdapterType())) { 202 throw new Exception( 203 "AuthAdapter (of class '{$adapter_class}') has an invalid ". 204 "implementation: no adapter type."); 205 } 206 207 if (!strlen($adapter->getAdapterDomain())) { 208 throw new Exception( 209 "AuthAdapter (of class '{$adapter_class}') has an invalid ". 210 "implementation: no adapter domain."); 211 } 212 213 $account = id(new PhabricatorExternalAccount())->loadOneWhere( 214 'accountType = %s AND accountDomain = %s AND accountID = %s', 215 $adapter->getAdapterType(), 216 $adapter->getAdapterDomain(), 217 $account_id); 218 if (!$account) { 219 $account = id(new PhabricatorExternalAccount()) 220 ->setAccountType($adapter->getAdapterType()) 221 ->setAccountDomain($adapter->getAdapterDomain()) 222 ->setAccountID($account_id); 223 } 224 225 $account->setUsername($adapter->getAccountName()); 226 $account->setRealName($adapter->getAccountRealName()); 227 $account->setEmail($adapter->getAccountEmail()); 228 $account->setAccountURI($adapter->getAccountURI()); 229 230 $account->setProfileImagePHID(null); 231 $image_uri = $adapter->getAccountImageURI(); 232 if ($image_uri) { 233 try { 234 $name = PhabricatorSlug::normalize($this->getProviderName()); 235 $name = $name.'-profile.jpg'; 236 237 // TODO: If the image has not changed, we do not need to make a new 238 // file entry for it, but there's no convenient way to do this with 239 // PhabricatorFile right now. The storage will get shared, so the impact 240 // here is negligible. 241 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 242 $image_file = PhabricatorFile::newFromFileDownload( 243 $image_uri, 244 array( 245 'name' => $name, 246 'canCDN' => true, 247 )); 248 unset($unguarded); 249 250 if ($image_file) { 251 $account->setProfileImagePHID($image_file->getPHID()); 252 } 253 } catch (Exception $ex) { 254 // Log this but proceed, it's not especially important that we 255 // be able to pull profile images. 256 phlog($ex); 257 } 258 } 259 260 $this->willSaveAccount($account); 261 262 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 263 $account->save(); 264 unset($unguarded); 265 266 return $account; 267 } 268 269 public function getLoginURI() { 270 $app = PhabricatorApplication::getByClass('PhabricatorAuthApplication'); 271 return $app->getApplicationURI('/login/'.$this->getProviderKey().'/'); 272 } 273 274 public function getSettingsURI() { 275 return '/settings/panel/external/'; 276 } 277 278 public function getStartURI() { 279 $app = PhabricatorApplication::getByClass('PhabricatorAuthApplication'); 280 $uri = $app->getApplicationURI('/start/'); 281 return $uri; 282 } 283 284 public function isDefaultRegistrationProvider() { 285 return false; 286 } 287 288 public function shouldRequireRegistrationPassword() { 289 return false; 290 } 291 292 public function getDefaultExternalAccount() { 293 throw new PhutilMethodNotImplementedException(); 294 } 295 296 public function getLoginOrder() { 297 return '500-'.$this->getProviderName(); 298 } 299 300 protected function getLoginIcon() { 301 return 'Generic'; 302 } 303 304 public function isLoginFormAButton() { 305 return false; 306 } 307 308 public function renderConfigPropertyTransactionTitle( 309 PhabricatorAuthProviderConfigTransaction $xaction) { 310 311 return null; 312 } 313 314 public function readFormValuesFromProvider() { 315 return array(); 316 } 317 318 public function readFormValuesFromRequest(AphrontRequest $request) { 319 return array(); 320 } 321 322 public function processEditForm( 323 AphrontRequest $request, 324 array $values) { 325 326 $errors = array(); 327 $issues = array(); 328 329 return array($errors, $issues, $values); 330 } 331 332 public function extendEditForm( 333 AphrontRequest $request, 334 AphrontFormView $form, 335 array $values, 336 array $issues) { 337 338 return; 339 } 340 341 public function willRenderLinkedAccount( 342 PhabricatorUser $viewer, 343 PHUIObjectItemView $item, 344 PhabricatorExternalAccount $account) { 345 346 $account_view = id(new PhabricatorAuthAccountView()) 347 ->setExternalAccount($account) 348 ->setAuthProvider($this); 349 350 $item->appendChild( 351 phutil_tag( 352 'div', 353 array( 354 'class' => 'mmr mml mst mmb', 355 ), 356 $account_view)); 357 } 358 359 /** 360 * Return true to use a two-step configuration (setup, configure) instead of 361 * the default single-step configuration. In practice, this means that 362 * creating a new provider instance will redirect back to the edit page 363 * instead of the provider list. 364 * 365 * @return bool True if this provider uses two-step configuration. 366 */ 367 public function hasSetupStep() { 368 return false; 369 } 370 371 /** 372 * Render a standard login/register button element. 373 * 374 * The `$attributes` parameter takes these keys: 375 * 376 * - `uri`: URI the button should take the user to when clicked. 377 * - `method`: Optional HTTP method the button should use, defaults to GET. 378 * 379 * @param AphrontRequest HTTP request. 380 * @param string Request mode string. 381 * @param map Additional parameters, see above. 382 * @return wild Login button. 383 */ 384 protected function renderStandardLoginButton( 385 AphrontRequest $request, 386 $mode, 387 array $attributes = array()) { 388 389 PhutilTypeSpec::checkMap( 390 $attributes, 391 array( 392 'method' => 'optional string', 393 'uri' => 'string', 394 'sigil' => 'optional string', 395 )); 396 397 $viewer = $request->getUser(); 398 $adapter = $this->getAdapter(); 399 400 if ($mode == 'link') { 401 $button_text = pht('Link External Account'); 402 } else if ($mode == 'refresh') { 403 $button_text = pht('Refresh Account Link'); 404 } else if ($this->shouldAllowRegistration()) { 405 $button_text = pht('Login or Register'); 406 } else { 407 $button_text = pht('Login'); 408 } 409 410 $icon = id(new PHUIIconView()) 411 ->setSpriteSheet(PHUIIconView::SPRITE_LOGIN) 412 ->setSpriteIcon($this->getLoginIcon()); 413 414 $button = id(new PHUIButtonView()) 415 ->setSize(PHUIButtonView::BIG) 416 ->setColor(PHUIButtonView::GREY) 417 ->setIcon($icon) 418 ->setText($button_text) 419 ->setSubtext($this->getProviderName()); 420 421 $uri = $attributes['uri']; 422 $uri = new PhutilURI($uri); 423 $params = $uri->getQueryParams(); 424 $uri->setQueryParams(array()); 425 426 $content = array($button); 427 428 foreach ($params as $key => $value) { 429 $content[] = phutil_tag( 430 'input', 431 array( 432 'type' => 'hidden', 433 'name' => $key, 434 'value' => $value, 435 )); 436 } 437 438 return phabricator_form( 439 $viewer, 440 array( 441 'method' => idx($attributes, 'method', 'GET'), 442 'action' => (string)$uri, 443 'sigil' => idx($attributes, 'sigil'), 444 ), 445 $content); 446 } 447 448 public function renderConfigurationFooter() { 449 return null; 450 } 451 452 protected function getAuthCSRFCode(AphrontRequest $request) { 453 $phcid = $request->getCookie(PhabricatorCookies::COOKIE_CLIENTID); 454 if (!strlen($phcid)) { 455 throw new Exception( 456 pht( 457 'Your browser did not submit a "%s" cookie with client state '. 458 'information in the request. Check that cookies are enabled. '. 459 'If this problem persists, you may need to clear your cookies.', 460 PhabricatorCookies::COOKIE_CLIENTID)); 461 } 462 463 return PhabricatorHash::digest($phcid); 464 } 465 466 protected function verifyAuthCSRFCode(AphrontRequest $request, $actual) { 467 $expect = $this->getAuthCSRFCode($request); 468 469 if (!strlen($actual)) { 470 throw new Exception( 471 pht( 472 'The authentication provider did not return a client state '. 473 'parameter in its response, but one was expected. If this '. 474 'problem persists, you may need to clear your cookies.')); 475 } 476 477 if ($actual !== $expect) { 478 throw new Exception( 479 pht( 480 'The authentication provider did not return the correct client '. 481 'state parameter in its response. If this problem persists, you may '. 482 'need to clear your cookies.')); 483 } 484 } 485 486 }
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 |