diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -151,7 +151,7 @@ 'rsrc/css/sprite-conpherence.css' => '3b4a0487', 'rsrc/css/sprite-docs.css' => '5f65d0da', 'rsrc/css/sprite-gradient.css' => '4bdb98a7', - 'rsrc/css/sprite-login.css' => '878ee4d8', + 'rsrc/css/sprite-login.css' => '3c811008', 'rsrc/css/sprite-main-header.css' => '92720ee2', 'rsrc/css/sprite-menu.css' => '28281e16', 'rsrc/css/sprite-minicons.css' => 'df4f76fe', @@ -816,7 +816,7 @@ 'sprite-conpherence-css' => '3b4a0487', 'sprite-docs-css' => '5f65d0da', 'sprite-gradient-css' => '4bdb98a7', - 'sprite-login-css' => '878ee4d8', + 'sprite-login-css' => '3c811008', 'sprite-main-header-css' => '92720ee2', 'sprite-menu-css' => '28281e16', 'sprite-minicons-css' => 'df4f76fe', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1279,6 +1279,7 @@ 'PhabricatorAuthProviderOAuth1' => 'applications/auth/provider/PhabricatorAuthProviderOAuth1.php', 'PhabricatorAuthProviderOAuth1Bitbucket' => 'applications/auth/provider/PhabricatorAuthProviderOAuth1Bitbucket.php', 'PhabricatorAuthProviderOAuth1JIRA' => 'applications/auth/provider/PhabricatorAuthProviderOAuth1JIRA.php', + 'PhabricatorAuthProviderOAuth1MediaWiki' => 'applications/auth/provider/PhabricatorAuthProviderOAuth1MediaWiki.php', 'PhabricatorAuthProviderOAuth1Twitter' => 'applications/auth/provider/PhabricatorAuthProviderOAuth1Twitter.php', 'PhabricatorAuthProviderOAuth2' => 'applications/auth/provider/PhabricatorAuthProviderOAuth2.php', 'PhabricatorAuthProviderOAuthAmazon' => 'applications/auth/provider/PhabricatorAuthProviderOAuthAmazon.php', @@ -4076,6 +4077,7 @@ 'PhabricatorAuthProviderOAuth1' => 'PhabricatorAuthProviderOAuth', 'PhabricatorAuthProviderOAuth1Bitbucket' => 'PhabricatorAuthProviderOAuth1', 'PhabricatorAuthProviderOAuth1JIRA' => 'PhabricatorAuthProviderOAuth1', + 'PhabricatorAuthProviderOAuth1MediaWiki' => 'PhabricatorAuthProviderOAuth1', 'PhabricatorAuthProviderOAuth1Twitter' => 'PhabricatorAuthProviderOAuth1', 'PhabricatorAuthProviderOAuth2' => 'PhabricatorAuthProviderOAuth', 'PhabricatorAuthProviderOAuthAmazon' => 'PhabricatorAuthProviderOAuth2', diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php --- a/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php +++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1.php @@ -52,9 +52,11 @@ $client_code = $this->getAuthCSRFCode($request); - $callback_uri = $adapter->getCallbackURI(); - $callback_uri = $callback_uri.$client_code.'/'; - $adapter->setCallbackURI($callback_uri); + if ($adapter->shouldAddCSRFTokenToCallbackURI()) { + $callback_uri = $adapter->getCallbackURI(); + $callback_uri = $callback_uri.$client_code.'/'; + $adapter->setCallbackURI($callback_uri); + } $uri = $adapter->getClientRedirectURI(); @@ -75,8 +77,9 @@ // NOTE: You can get here via GET, this should probably be a bit more // user friendly. - - $this->verifyAuthCSRFCode($request, $controller->getExtraURIData()); + if ($adapter->shouldAddCSRFTokenToCallbackURI()) { + $this->verifyAuthCSRFCode($request, $controller->getExtraURIData()); + } $token = $request->getStr('oauth_token'); $verifier = $request->getStr('oauth_verifier'); diff --git a/src/applications/auth/provider/PhabricatorAuthProviderOAuth1MediaWiki.php b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1MediaWiki.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/provider/PhabricatorAuthProviderOAuth1MediaWiki.php @@ -0,0 +1,212 @@ +getProviderConfig(); + return array( + self::PROPERTY_MEDIAWIKI_NAME => + $this->getProviderDomain(), + self::PROPERTY_MEDIAWIKI_URI => + $config->getProperty(self::PROPERTY_MEDIAWIKI_URI), + self::PROPERTY_CONSUMER_KEY => + $config->getProperty(self::PROPERTY_CONSUMER_KEY), + self::PROPERTY_CONSUMER_SECRET => + $config->getProperty(self::PROPERTY_CONSUMER_SECRET), + ); + } + + public function readFormValuesFromRequest(AphrontRequest $request) { + $is_setup = $this->isSetup(); + if ($is_setup) { + $name = $request->getStr(self::PROPERTY_MEDIAWIKI_NAME); + } else { + $name = $this->getProviderDomain(); + } + + return array( + self::PROPERTY_MEDIAWIKI_NAME => $name, + self::PROPERTY_MEDIAWIKI_URI => + $request->getStr(self::PROPERTY_MEDIAWIKI_URI), + self::PROPERTY_CONSUMER_KEY => + $request->getStr(self::PROPERTY_CONSUMER_KEY), + self::PROPERTY_CONSUMER_SECRET => + $request->getStr(self::PROPERTY_CONSUMER_SECRET), + ); + } + + public function getProviderName() { + return pht('MediaWiki'); + } + + protected function getProviderConfigurationHelp() { + $login_uri = PhabricatorEnv::getURI($this->getLoginURI()); + + return pht( + "To configure MediaWiki OAuth, create a new \"consumer\" here:". + "\n\n". + "https://www.mediawiki.org/wiki/". + "Special:OAuthConsumerRegistration/propose". + "\n\n". + "When creating your application, use these settings:". + "\n\n". + " - **Callback URL:** Set this to: `%s`". + "\n\n". + "After you register the consumer, a **Consumer Key** and ". + "**Consumer Secret** will be provided to you by MediaWiki. ". + "To complete the configuration, copy the provided keys into ". + "the corresponding fields above.", + $login_uri); + } + + protected function newOAuthAdapter() { + $config = $this->getProviderConfig(); + + return id(new PhutilAuthAdapterOAuthMediaWiki()) + ->setAdapterDomain($config->getProviderDomain()) + ->setMediaWikiBaseURI( + $config->getProperty(self::PROPERTY_MEDIAWIKI_URI)); + } + + protected function getLoginIcon() { + return 'MediaWiki'; + } + + private function isSetup() { + return !$this->getProviderConfig()->getID(); + } + + public function processEditForm( + AphrontRequest $request, + array $values) { + $errors = array(); + $issues = array(); + + $is_setup = $this->isSetup(); + + $key_name = self::PROPERTY_MEDIAWIKI_NAME; + $key_uri = self::PROPERTY_MEDIAWIKI_URI; + $key_secret = self::PROPERTY_CONSUMER_SECRET; + $key_consumer = self::PROPERTY_CONSUMER_KEY; + + if (!strlen($values[$key_uri])) { + $errors[] = pht('MediaWiki base URI is required.'); + $issues[$key_uri] = pht('Required'); + } else { + $uri = new PhutilURI($values[$key_uri]); + if (!$uri->getProtocol()) { + $errors[] = pht( + 'MediaWiki base URI should include protocol ' + .'(like "https://").'); + $issues[$key_uri] = pht('Invalid'); + } + } + + if (!$errors) { + $config = $this->getProviderConfig(); + if ($is_setup) { + $config->setProviderDomain($values[$key_name]); + $config->setProperty($key_name, $values[$key_name]); + } + + $config->setProperty($key_uri, $values[$key_uri]); + $config->setProperty($key_secret, $values[$key_secret]); + $config->setProperty($key_consumer, $values[$key_consumer]); + } + + return array($errors, $issues, $values); + } + + public function extendEditForm( + AphrontRequest $request, + AphrontFormView $form, + array $values, + array $issues) { + + $is_setup = $this->isSetup(); + + $e_required = $request->isFormPost() ? null : true; + + $v_name = $values[self::PROPERTY_MEDIAWIKI_NAME]; + if ($is_setup) { + $e_name = idx($issues, self::PROPERTY_MEDIAWIKI_NAME, $e_required); + } else { + $e_name = null; + } + + $v_uri = $values[self::PROPERTY_MEDIAWIKI_URI]; + $e_uri = idx($issues, self::PROPERTY_MEDIAWIKI_URI, $e_required); + + $config = $this->getProviderConfig(); + + if ($is_setup) { + $form + ->appendRemarkupInstructions( + pht( + "**MediaWiki Instance Name**\n\n". + "Choose a permanent name for this instance of MediaWiki.". + "Phabricator uses this name internally to keep track of ". + "this instance of MediaWiki, in case the URL changes later.". + "\n\n". + "Use lowercase letters, digits, and period. For example: ". + "\n\n`mediawiki`, `mediawiki.mycompany` ". + "or `mediawiki.engineering` are reasonable names.")) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('MediaWiki Instance Name')) + ->setValue($v_name) + ->setName(self::PROPERTY_MEDIAWIKI_NAME) + ->setError($e_name)); + } else { + $form + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel(pht('MediaWiki Instance Name')) + ->setValue($v_name)); + } + + $form + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('MediaWiki Base URI')) + ->setValue($v_uri) + ->setName(self::PROPERTY_MEDIAWIKI_URI) + ->setCaption(pht('The URI where MediaWiki is installed.')) + ->setError($e_uri)) + ->appendRemarkupInstructions( + pht( + 'NOTE: Copy the keys generated by the MediaWiki OAuth'. + ' consumer registration and paste them here.')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Consumer Key')) + ->setName(self::PROPERTY_CONSUMER_KEY) + ->setValue( + $config->getProperty(self::PROPERTY_CONSUMER_KEY))) + ->appendChild( + id(new AphrontFormTextControl()) + ->setLabel(pht('Secret Key')) + ->setName(self::PROPERTY_CONSUMER_SECRET) + ->setValue( + $config->getProperty(self::PROPERTY_CONSUMER_SECRET))); + } + + public static function getMediaWikiProvider() { + $providers = self::getAllEnabledProviders(); + + foreach ($providers as $provider) { + if ($provider instanceof PhabricatorAuthProviderOAuth1MediaWiki) { + return $provider; + } + } + + return null; + } + +}