diff --git a/conf/default.conf.php b/conf/default.conf.php index e876369668..f604a7a9c4 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -329,7 +329,7 @@ return array( 'account.minimum-password-length' => 8, -// -- Facebook ------------------------------------------------------------ // +// -- Facebook OAuth -------------------------------------------------------- // // Can users use Facebook credentials to login to Phabricator? 'facebook.auth-enabled' => false, @@ -348,7 +348,7 @@ return array( 'facebook.application-secret' => null, -// -- GitHub ---------------------------------------------------------------- // +// -- GitHub OAuth ---------------------------------------------------------- // // Can users use GitHub credentials to login to Phabricator? 'github.auth-enabled' => false, @@ -367,7 +367,7 @@ return array( 'github.application-secret' => null, -// -- Google ---------------------------------------------------------------- // +// -- Google OAuth ---------------------------------------------------------- // // Can users use Google credentials to login to Phabricator? 'google.auth-enabled' => false, @@ -385,6 +385,30 @@ return array( // The Google "Client Secret" to use for Google API access. 'google.application-secret' => null, +// -- Phabricator OAuth ----------------------------------------------------- // + + // Meta-town -- Phabricator is itself an OAuth Provider + // TODO -- T887 -- make this support multiple Phabricator instances! + + // The URI of the Phabricator instance to use as an OAuth server. + 'phabricator.oauth-uri' => null, + + // Can users use Phabricator credentials to login to Phabricator? + 'phabricator.auth-enabled' => false, + + // Can users use Phabricator credentials to create new Phabricator accounts? + 'phabricator.registration-enabled' => true, + + // Are Phabricator accounts permanently linked to Phabricator accounts, or can + // the user unlink them? + 'phabricator.auth-permanent' => false, + + // The Phabricator "Client ID" to use for Phabricator API access. + 'phabricator.application-id' => null, + + // The Phabricator "Client Secret" to use for Phabricator API access. + 'phabricator.application-secret' => null, + // -- Recaptcha ------------------------------------------------------------- // // Is Recaptcha enabled? If disabled, captchas will not appear. You should diff --git a/resources/sql/patches/106.chatlog.sql b/resources/sql/patches/106.chatlog.sql index ebc0e4b1b0..437c74c003 100644 --- a/resources/sql/patches/106.chatlog.sql +++ b/resources/sql/patches/106.chatlog.sql @@ -8,4 +8,4 @@ CREATE TABLE phabricator_chatlog.chatlog_event ( message LONGBLOB NOT NULL, loggedByPHID VARCHAR(64) BINARY NOT NULL, KEY (channel, epoch) -); \ No newline at end of file +); diff --git a/resources/sql/patches/107.oauthserver.sql b/resources/sql/patches/107.oauthserver.sql new file mode 100644 index 0000000000..4598762a4a --- /dev/null +++ b/resources/sql/patches/107.oauthserver.sql @@ -0,0 +1,51 @@ +CREATE DATABASE IF NOT EXISTS `phabricator_oauth_server`; + +CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserverclient` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varchar(64) BINARY NOT NULL, + `name` varchar(255) NOT NULL, + `secret` varchar(32) NOT NULL, + `redirectURI` varchar(255) NOT NULL, + `creatorPHID` varchar(64) BINARY NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `phid` (`phid`) +) ENGINE=InnoDB; + +CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthclientauthorization` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `phid` varchar(64) BINARY NOT NULL, + `userPHID` varchar(64) BINARY NOT NULL, + `clientPHID` varchar(64) BINARY NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `phid` (`phid`), + UNIQUE KEY `userPHID` (`userPHID`,`clientPHID`) +) ENGINE=InnoDB; + +CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserverauthorizationcode` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `code` varchar(32) NOT NULL, + `clientPHID` varchar(64) BINARY NOT NULL, + `clientSecret` varchar(32) NOT NULL, + `userPHID` varchar(64) BINARY NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB; + +CREATE TABLE `phabricator_oauth_server`.`oauth_server_oauthserveraccesstoken` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `token` varchar(32) NOT NULL, + `userPHID` varchar(64) BINARY NOT NULL, + `clientPHID` varchar(64) BINARY NOT NULL, + `dateExpires` int(10) unsigned NOT NULL, + `dateCreated` int(10) unsigned NOT NULL, + `dateModified` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `token` (`token`) +) ENGINE=InnoDB; + diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 40b384796e..2716e0d4c5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -597,6 +597,7 @@ phutil_register_library_map(array( 'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/sendgridreceive', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/mysql', + 'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/clientauthorization', 'PhabricatorOAuthDefaultRegistrationController' => 'applications/auth/controller/oauthregistration/default', 'PhabricatorOAuthDiagnosticsController' => 'applications/auth/controller/oauthdiagnostics', 'PhabricatorOAuthFailureView' => 'applications/auth/view/oauthfailure', @@ -605,7 +606,17 @@ phutil_register_library_map(array( 'PhabricatorOAuthProviderFacebook' => 'applications/auth/oauth/provider/facebook', 'PhabricatorOAuthProviderGitHub' => 'applications/auth/oauth/provider/github', 'PhabricatorOAuthProviderGoogle' => 'applications/auth/oauth/provider/google', + 'PhabricatorOAuthProviderPhabricator' => 'applications/auth/oauth/provider/phabricator', 'PhabricatorOAuthRegistrationController' => 'applications/auth/controller/oauthregistration/base', + 'PhabricatorOAuthResponse' => 'applications/oauthserver/response', + 'PhabricatorOAuthServer' => 'applications/oauthserver/server', + 'PhabricatorOAuthServerAccessToken' => 'applications/oauthserver/storage/accesstoken', + 'PhabricatorOAuthServerAuthController' => 'applications/oauthserver/controller/auth', + 'PhabricatorOAuthServerAuthorizationCode' => 'applications/oauthserver/storage/authorizationcode', + 'PhabricatorOAuthServerClient' => 'applications/oauthserver/storage/client', + 'PhabricatorOAuthServerDAO' => 'applications/oauthserver/storage/base', + 'PhabricatorOAuthServerTestController' => 'applications/oauthserver/controller/test', + 'PhabricatorOAuthServerTokenController' => 'applications/oauthserver/controller/token', 'PhabricatorOAuthUnlinkController' => 'applications/auth/controller/unlink', 'PhabricatorObjectAttachmentEditor' => 'applications/search/editor/attach', 'PhabricatorObjectGraph' => 'applications/phid/graph', @@ -1324,6 +1335,7 @@ phutil_register_library_map(array( 'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController', 'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController', 'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine', + 'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO', 'PhabricatorOAuthDefaultRegistrationController' => 'PhabricatorOAuthRegistrationController', 'PhabricatorOAuthDiagnosticsController' => 'PhabricatorAuthController', 'PhabricatorOAuthFailureView' => 'AphrontView', @@ -1331,7 +1343,16 @@ phutil_register_library_map(array( 'PhabricatorOAuthProviderFacebook' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthProviderGitHub' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthProviderGoogle' => 'PhabricatorOAuthProvider', + 'PhabricatorOAuthProviderPhabricator' => 'PhabricatorOAuthProvider', 'PhabricatorOAuthRegistrationController' => 'PhabricatorAuthController', + 'PhabricatorOAuthResponse' => 'AphrontResponse', + 'PhabricatorOAuthServerAccessToken' => 'PhabricatorOAuthServerDAO', + 'PhabricatorOAuthServerAuthController' => 'PhabricatorAuthController', + 'PhabricatorOAuthServerAuthorizationCode' => 'PhabricatorOAuthServerDAO', + 'PhabricatorOAuthServerClient' => 'PhabricatorOAuthServerDAO', + 'PhabricatorOAuthServerDAO' => 'PhabricatorLiskDAO', + 'PhabricatorOAuthServerTestController' => 'PhabricatorAuthController', + 'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController', 'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController', 'PhabricatorObjectGraph' => 'AbstractDirectedGraph', 'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index d9815e4091..09be17df02 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -151,6 +151,12 @@ class AphrontDefaultApplicationConfiguration ), ), + '/oauthserver/' => array( + 'auth/' => 'PhabricatorOAuthServerAuthController', + 'token/' => 'PhabricatorOAuthServerTokenController', + 'test/' => 'PhabricatorOAuthServerTestController', + ), + '/xhprof/' => array( 'profile/(?P[^/]+)/$' => 'PhabricatorXHProfProfileController', ), diff --git a/src/aphront/response/ajax/AphrontAjaxResponse.php b/src/aphront/response/ajax/AphrontAjaxResponse.php index a22cbc6eeb..2195b02aad 100644 --- a/src/aphront/response/ajax/AphrontAjaxResponse.php +++ b/src/aphront/response/ajax/AphrontAjaxResponse.php @@ -35,9 +35,8 @@ final class AphrontAjaxResponse extends AphrontResponse { $this->content, $this->error); - return $this->encodeJSONForHTTPResponse( - $object, - $use_javelin_shield = true); + $response_json = $this->encodeJSONForHTTPResponse($object); + return $this->addJSONShield($response_json, $use_javelin_shield = true); } public function getHeaders() { diff --git a/src/aphront/response/base/AphrontResponse.php b/src/aphront/response/base/AphrontResponse.php index 6c32b7f744..c2b1e40f41 100644 --- a/src/aphront/response/base/AphrontResponse.php +++ b/src/aphront/response/base/AphrontResponse.php @@ -84,6 +84,11 @@ abstract class AphrontResponse { array('\u003c', '\u003e'), $response); + return $response; + } + + protected function addJSONShield($json_response, $use_javelin_shield) { + // Add a shield to prevent "JSON Hijacking" attacks where an attacker // requests a JSON response using a normal