Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -1085,6 +1085,7 @@ 'PhabricatorAuthProviderOAuthTwitch' => 'applications/auth/provider/PhabricatorAuthProviderOAuthTwitch.php', 'PhabricatorAuthProviderPassword' => 'applications/auth/provider/PhabricatorAuthProviderPassword.php', 'PhabricatorAuthProviderPersona' => 'applications/auth/provider/PhabricatorAuthProviderPersona.php', + 'PhabricatorAuthProviderShibboleth' => 'applications/auth/provider/PhabricatorAuthProviderShibboleth.php', 'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php', 'PhabricatorAuthStartController' => 'applications/auth/controller/PhabricatorAuthStartController.php', 'PhabricatorAuthUnlinkController' => 'applications/auth/controller/PhabricatorAuthUnlinkController.php', @@ -3423,6 +3424,7 @@ 'PhabricatorAuthProviderOAuthTwitch' => 'PhabricatorAuthProviderOAuth', 'PhabricatorAuthProviderPassword' => 'PhabricatorAuthProvider', 'PhabricatorAuthProviderPersona' => 'PhabricatorAuthProvider', + 'PhabricatorAuthProviderShibboleth' => 'PhabricatorAuthProvider', 'PhabricatorAuthRegisterController' => 'PhabricatorAuthController', 'PhabricatorAuthStartController' => 'PhabricatorAuthController', 'PhabricatorAuthUnlinkController' => 'PhabricatorAuthController', Index: src/applications/auth/provider/PhabricatorAuthProviderShibboleth.php =================================================================== --- /dev/null +++ src/applications/auth/provider/PhabricatorAuthProviderShibboleth.php @@ -0,0 +1,246 @@ +adapter) { + $conf = $this->getProviderConfig(); + + + $adapter = id(new PhutilAuthAdapterShibboleth()) + ->setShibSessionIdField( + $conf->getProperty(self::KEY_SHIB_SESSION_ID_FIELD)) + ->setShibApplicationIdField( + $conf->getProperty(self::KEY_SHIB_APPLICATION_ID_FIELD)) + ->setUseridField( + $conf->getProperty(self::KEY_USERID_FIELD)) + ->setUsernameField( + $conf->getProperty(self::KEY_USERNAME_FIELD)) + ->setRealnameField( + $conf->getProperty(self::KEY_REALNAME_FIELD)) + ->setEmailField( + $conf->getProperty(self::KEY_EMAIL_FIELD)) + ->setPageURIPattern( + $conf->getProperty(self::KEY_PAGE_URI_PATTERN)) + ->setImageURIPattern( + $conf->getProperty(self::KEY_IMAGE_URI_PATTERN)); + $this->adapter = $adapter; + } + return $this->adapter; + } + + protected function renderLoginForm(AphrontRequest $request, $mode) { + $viewer = $request->getUser(); + + $dialog = id(new AphrontDialogView()) + ->setSubmitURI($this->getLoginURI()) + ->setUser($viewer); + + if ($mode == 'link') { + $dialog->setTitle(pht('Link Shibboleth Account')); + $dialog->addSubmitButton(pht('Link Accounts')); + $dialog->addCancelButton($this->getSettingsURI()); + } else if ($mode == 'refresh') { + $dialog->setTitle(pht('Refresh Shibboleth Account')); + $dialog->addSubmitButton(pht('Refresh Account')); + $dialog->addCancelButton($this->getSettingsURI()); + } else { + if ($this->shouldAllowRegistration()) { + $dialog->setTitle(pht('Login or Register with Shibboleth')); + $dialog->addSubmitButton(pht('Login or Register')); + } else { + $dialog->setTitle(pht('Login with Shibboleth')); + $dialog->addSubmitButton(pht('Login')); + } + if ($mode == 'login') { + $dialog->addCancelButton($this->getStartURI()); + } + } + + $errors = array(); + if ($request->isHTTPPost()) { + $errors[] = pht('Invalid Shibboleth session.'); + } + + if ($errors) { + $errors = id(new AphrontErrorView())->setErrors($errors); + } + + $dialog->appendChild($errors); + + return $dialog; + } + + public function processLoginRequest( + PhabricatorAuthLoginController $controller) { + + $request = $controller->getRequest(); + $response = null; + $account = null; + + $adapter = $this->getAdapter(); + $headers = array(); + $header_names = $adapter->getHeaderNames(); + foreach ($header_names as $h) { + $headers[$h] = $request->getHTTPHeader($h); + } + if (! $adapter->setUserDataFromRequest($headers)) { + $response = $controller->buildProviderPageResponse( + $this, + $this->renderLoginForm($request, 'login')); + return array($account, $response); + } + + $account_id = $adapter->getAccountID(); + + return array($this->loadOrCreateAccount($account_id), $response); + } + + + const KEY_SHIB_SESSION_ID_FIELD = 'shibboleth:session_id_field'; + const KEY_SHIB_APPLICATION_ID_FIELD = 'shibboleth:application_id_field'; + const KEY_USERID_FIELD = 'shibboleth:userid_field'; + const KEY_USERNAME_FIELD = 'shibboleth:username_field'; + const KEY_REALNAME_FIELD = 'shibboleth:realname_field'; + const KEY_EMAIL_FIELD = 'shibboleth:email_field'; + const KEY_PAGE_URI_PATTERN = 'shibboleth:page_uri_pattern'; + const KEY_IMAGE_URI_PATTERN = 'shibboleth:image_uri_pattern'; + + private function getPropertyKeys() { + return array_keys($this->getPropertyLabels()); + } + + private function getPropertyLabels() { + return array( + self::KEY_SHIB_SESSION_ID_FIELD => pht('Session ID field name'), + self::KEY_SHIB_APPLICATION_ID_FIELD => pht('Application ID field name'), + self::KEY_USERID_FIELD => pht('User ID field name'), + self::KEY_USERNAME_FIELD => pht('Username field name'), + self::KEY_REALNAME_FIELD => pht('Real name field name'), + self::KEY_EMAIL_FIELD => pht('User email field name'), + self::KEY_PAGE_URI_PATTERN => pht('User page URI pattern'), + self::KEY_IMAGE_URI_PATTERN => pht('User image URI pattern'), + ); + } + + public function readFormValuesFromProvider() { + $properties = array(); + foreach ($this->getPropertyLabels() as $key => $ignored) { + $properties[$key] = $this->getProviderConfig()->getProperty($key); + } + return $properties; + } + + public function readFormValuesFromRequest(AphrontRequest $request) { + $values = array(); + foreach ($this->getPropertyKeys() as $key) { + $values[$key] = $request->getStr($key); + } + + return $values; + } + + public function processEditForm( + AphrontRequest $request, + array $values) { + $errors = array(); + $issues = array(); + return array($errors, $issues, $values); + } + + public function extendEditForm( + AphrontRequest $request, + AphrontFormView $form, + array $values, + array $issues) { + + $labels = $this->getPropertyLabels(); + + $captions = array( + self::KEY_SHIB_SESSION_ID_FIELD => pht('Session ID field name'), + self::KEY_SHIB_APPLICATION_ID_FIELD => pht('Application ID field name'), + self::KEY_USERID_FIELD => pht('User ID field name'), + self::KEY_USERNAME_FIELD => pht('Username field name'), + self::KEY_REALNAME_FIELD => pht('Real name field name'), + self::KEY_EMAIL_FIELD => pht('User email field name'), + self::KEY_PAGE_URI_PATTERN => pht('User page URI pattern'), + self::KEY_IMAGE_URI_PATTERN => pht('User image URI pattern'), + ); + + foreach ($labels as $key => $label) { + $caption = idx($captions, $key); + $value = idx($values, $key); + + $control = null; + $control = id(new AphrontFormTextControl()) + ->setName($key) + ->setLabel($label) + ->setCaption($caption) + ->setValue($value); + + $form->appendChild($control); + } + } + + public function renderConfigPropertyTransactionTitle( + PhabricatorAuthProviderConfigTransaction $xaction) { + + $author_phid = $xaction->getAuthorPHID(); + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + $key = $xaction->getMetadataValue( + PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY); + + $labels = $this->getPropertyLabels(); + if (isset($labels[$key])) { + $label = $labels[$key]; + + if (!strlen($old)) { + return pht( + '%s set the "%s" value to "%s".', + $xaction->renderHandleLink($author_phid), + $label, + $new); + } else { + return pht( + '%s changed the "%s" value from "%s" to "%s".', + $xaction->renderHandleLink($author_phid), + $label, + $old, + $new); + } + } + + return parent::renderConfigPropertyTransactionTitle($xaction); + } + + public static function getShibbolethProvider() { + $providers = self::getAllEnabledProviders(); + + foreach ($providers as $provider) { + if ($provider instanceof PhabricatorAuthProviderShibboleth) { + return $provider; + } + } + + return null; + } + +}