@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.

Allow OAuth applications to be disabled instead of destroyed

Summary: Ref T7303. This interaction is very oldschool; modernize it to enable/disable instead of "nuke from orbit".

Test Plan:
- Enabled applications.
- Disabled applications.
- Viewed applications in list view.
- Generated new tokens.
- Tried to use a token from a disabled application (got rebuffed).
- Tried to use a token from an enabled application (worked fine).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T7303

Differential Revision: https://secure.phabricator.com/D15620

+185 -98
+2
resources/sql/autopatches/20160405.oauth.2.disable.sql
··· 1 + ALTER TABLE {$NAMESPACE}_oauth_server.oauth_server_oauthserverclient 2 + ADD isDisabled BOOL NOT NULL;
+2 -2
src/__phutil_library_map__.php
··· 2702 2702 'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php', 2703 2703 'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php', 2704 2704 'PhabricatorOAuthClientController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientController.php', 2705 - 'PhabricatorOAuthClientDeleteController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php', 2705 + 'PhabricatorOAuthClientDisableController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php', 2706 2706 'PhabricatorOAuthClientEditController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientEditController.php', 2707 2707 'PhabricatorOAuthClientListController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientListController.php', 2708 2708 'PhabricatorOAuthClientSecretController' => 'applications/oauthserver/controller/client/PhabricatorOAuthClientSecretController.php', ··· 7194 7194 ), 7195 7195 'PhabricatorOAuthClientAuthorizationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7196 7196 'PhabricatorOAuthClientController' => 'PhabricatorOAuthServerController', 7197 - 'PhabricatorOAuthClientDeleteController' => 'PhabricatorOAuthClientController', 7197 + 'PhabricatorOAuthClientDisableController' => 'PhabricatorOAuthClientController', 7198 7198 'PhabricatorOAuthClientEditController' => 'PhabricatorOAuthClientController', 7199 7199 'PhabricatorOAuthClientListController' => 'PhabricatorOAuthClientController', 7200 7200 'PhabricatorOAuthClientSecretController' => 'PhabricatorOAuthClientController',
+5
src/applications/oauthserver/PhabricatorOAuthServer.php
··· 172 172 return null; 173 173 } 174 174 175 + $application = $authorization->getClient(); 176 + if ($application->getIsDisabled()) { 177 + return null; 178 + } 179 + 175 180 // TODO: This should probably be reworked; expiration should be an 176 181 // exclusive property of the token. For now, this logic reads: tokens for 177 182 // authorizations with "offline_access" never expire.
+2 -2
src/applications/oauthserver/application/PhabricatorOAuthServerApplication.php
··· 53 53 'token/' => 'PhabricatorOAuthServerTokenController', 54 54 $this->getEditRoutePattern('edit/') => 55 55 'PhabricatorOAuthClientEditController', 56 - 'client/' => array( 57 - 'delete/(?P<id>\d+)/' => 'PhabricatorOAuthClientDeleteController', 56 + 'client/' => array( 57 + 'disable/(?P<id>\d+)/' => 'PhabricatorOAuthClientDisableController', 58 58 'view/(?P<id>\d+)/' => 'PhabricatorOAuthClientViewController', 59 59 'secret/(?P<id>\d+)/' => 'PhabricatorOAuthClientSecretController', 60 60 'test/(?P<id>\d+)/' => 'PhabricatorOAuthClientTestController',
+9
src/applications/oauthserver/controller/PhabricatorOAuthServerAuthController.php
··· 62 62 phutil_tag('strong', array(), 'client_id'))); 63 63 } 64 64 65 + if ($client->getIsDisabled()) { 66 + return $this->buildErrorResponse( 67 + 'invalid_request', 68 + pht('Application Disabled'), 69 + pht( 70 + 'The %s OAuth application has been disabled.', 71 + phutil_tag('strong', array(), 'client_id'))); 72 + } 73 + 65 74 $name = $client->getName(); 66 75 $server->setClient($client); 67 76 if ($redirect_uri) {
+15 -4
src/applications/oauthserver/controller/PhabricatorOAuthServerTokenController.php
··· 67 67 $response->setError('invalid_grant'); 68 68 $response->setErrorDescription( 69 69 pht( 70 - 'Authorization code %d not found.', 70 + 'Authorization code %s not found.', 71 71 $code)); 72 72 return $response; 73 73 } ··· 102 102 $response->setError('invalid_client'); 103 103 $response->setErrorDescription( 104 104 pht( 105 - 'Client with %s %d not found.', 105 + 'Client with %s %s not found.', 106 106 'client_id', 107 107 $client_phid)); 108 108 return $response; 109 109 } 110 + 111 + if ($client->getIsDisabled()) { 112 + $response->setError('invalid_client'); 113 + $response->setErrorDescription( 114 + pht( 115 + 'OAuth application "%s" has been disabled.', 116 + $client->getName())); 117 + 118 + return $response; 119 + } 120 + 110 121 $server->setClient($client); 111 122 112 123 $user_phid = $auth_code->getUserPHID(); ··· 116 127 $response->setError('invalid_grant'); 117 128 $response->setErrorDescription( 118 129 pht( 119 - 'User with PHID %d not found.', 130 + 'User with PHID %s not found.', 120 131 $user_phid)); 121 132 return $response; 122 133 } ··· 132 143 $response->setError('invalid_grant'); 133 144 $response->setErrorDescription( 134 145 pht( 135 - 'Invalid authorization code %d.', 146 + 'Invalid authorization code %s.', 136 147 $code)); 137 148 return $response; 138 149 }
-40
src/applications/oauthserver/controller/client/PhabricatorOAuthClientDeleteController.php
··· 1 - <?php 2 - 3 - final class PhabricatorOAuthClientDeleteController 4 - extends PhabricatorOAuthClientController { 5 - 6 - public function handleRequest(AphrontRequest $request) { 7 - $viewer = $this->getViewer(); 8 - 9 - $client = id(new PhabricatorOAuthServerClientQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($request->getURIData('id'))) 12 - ->requireCapabilities( 13 - array( 14 - PhabricatorPolicyCapability::CAN_VIEW, 15 - PhabricatorPolicyCapability::CAN_EDIT, 16 - )) 17 - ->executeOne(); 18 - if (!$client) { 19 - return new Aphront404Response(); 20 - } 21 - 22 - // TODO: This should be "disable", not "delete"! 23 - 24 - if ($request->isFormPost()) { 25 - $client->delete(); 26 - $app_uri = $this->getApplicationURI(); 27 - return id(new AphrontRedirectResponse())->setURI($app_uri); 28 - } 29 - 30 - return $this->newDialog() 31 - ->setTitle(pht('Delete OAuth Application?')) 32 - ->appendParagraph( 33 - pht( 34 - 'Really delete the OAuth application %s?', 35 - phutil_tag('strong', array(), $client->getName()))) 36 - ->addCancelButton($client->getViewURI()) 37 - ->addSubmitButton(pht('Delete Application')); 38 - } 39 - 40 - }
+67
src/applications/oauthserver/controller/client/PhabricatorOAuthClientDisableController.php
··· 1 + <?php 2 + 3 + final class PhabricatorOAuthClientDisableController 4 + extends PhabricatorOAuthClientController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 8 + 9 + $client = id(new PhabricatorOAuthServerClientQuery()) 10 + ->setViewer($viewer) 11 + ->withIDs(array($request->getURIData('id'))) 12 + ->requireCapabilities( 13 + array( 14 + PhabricatorPolicyCapability::CAN_VIEW, 15 + PhabricatorPolicyCapability::CAN_EDIT, 16 + )) 17 + ->executeOne(); 18 + if (!$client) { 19 + return new Aphront404Response(); 20 + } 21 + 22 + $done_uri = $client->getViewURI(); 23 + $is_disable = !$client->getIsDisabled(); 24 + 25 + if ($request->isFormPost()) { 26 + $xactions = array(); 27 + 28 + $xactions[] = id(new PhabricatorOAuthServerTransaction()) 29 + ->setTransactionType(PhabricatorOAuthServerTransaction::TYPE_DISABLED) 30 + ->setNewValue((int)$is_disable); 31 + 32 + $editor = id(new PhabricatorOAuthServerEditor()) 33 + ->setActor($viewer) 34 + ->setContentSourceFromRequest($request) 35 + ->setContinueOnNoEffect(true) 36 + ->setContinueOnMissingFields(true) 37 + ->applyTransactions($client, $xactions); 38 + 39 + return id(new AphrontRedirectResponse())->setURI($done_uri); 40 + } 41 + 42 + if ($is_disable) { 43 + $title = pht('Disable OAuth Application'); 44 + $body = pht( 45 + 'Really disable the %s OAuth application? Users will no longer be '. 46 + 'able to authenticate against it, nor access Phabricator using '. 47 + 'tokens generated by this application.', 48 + phutil_tag('strong', array(), $client->getName())); 49 + $button = pht('Disable Application'); 50 + } else { 51 + $title = pht('Enable OAuth Application'); 52 + $body = pht( 53 + 'Really enable the %s OAuth application? Users will be able to '. 54 + 'authenticate against it, and existing tokens will become usable '. 55 + 'again.', 56 + phutil_tag('strong', array(), $client->getName())); 57 + $button = pht('Enable Application'); 58 + } 59 + 60 + return $this->newDialog() 61 + ->setTitle($title) 62 + ->appendParagraph($body) 63 + ->addCancelButton($done_uri) 64 + ->addSubmitButton($button); 65 + } 66 + 67 + }
+30 -25
src/applications/oauthserver/controller/client/PhabricatorOAuthClientTestController.php
··· 15 15 return new Aphront404Response(); 16 16 } 17 17 18 - $view_uri = $client->getViewURI(); 19 - 20 - // Look for an existing authorization. 21 - $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) 22 - ->setViewer($viewer) 23 - ->withUserPHIDs(array($viewer->getPHID())) 24 - ->withClientPHIDs(array($client->getPHID())) 25 - ->executeOne(); 26 - if ($authorization) { 27 - return $this->newDialog() 28 - ->setTitle(pht('Already Authorized')) 29 - ->appendParagraph( 30 - pht( 31 - 'You have already authorized this application to access your '. 32 - 'account.')) 33 - ->addCancelButton($view_uri, pht('Close')); 34 - } 18 + $done_uri = $client->getViewURI(); 35 19 36 20 if ($request->isFormPost()) { 37 21 $server = id(new PhabricatorOAuthServer()) 38 22 ->setUser($viewer) 39 23 ->setClient($client); 40 24 41 - $scope = array(); 42 - $authorization = $server->authorizeClient($scope); 25 + // Create an authorization if we don't already have one. 26 + $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) 27 + ->setViewer($viewer) 28 + ->withUserPHIDs(array($viewer->getPHID())) 29 + ->withClientPHIDs(array($client->getPHID())) 30 + ->executeOne(); 31 + if (!$authorization) { 32 + $scope = array(); 33 + $authorization = $server->authorizeClient($scope); 34 + } 35 + 36 + $access_token = $server->generateAccessToken(); 43 37 44 - $id = $authorization->getID(); 45 - $panel_uri = '/settings/panel/oauthorizations/?id='.$id; 38 + $form = id(new AphrontFormView()) 39 + ->setViewer($viewer) 40 + ->appendInstructions( 41 + pht( 42 + 'Keep this token private, it allows any bearer to access '. 43 + 'your account on behalf of this application.')) 44 + ->appendChild( 45 + id(new AphrontFormTextControl()) 46 + ->setLabel(pht('Token')) 47 + ->setValue($access_token->getToken())); 46 48 47 - return id(new AphrontRedirectResponse())->setURI($panel_uri); 49 + return $this->newDialog() 50 + ->setTitle(pht('OAuth Access Token')) 51 + ->appendForm($form) 52 + ->addCancelButton($done_uri, pht('Close')); 48 53 } 49 54 50 55 // TODO: It would be nice to put scope options in this dialog, maybe? ··· 53 58 ->setTitle(pht('Authorize Application?')) 54 59 ->appendParagraph( 55 60 pht( 56 - 'This will create an authorization, permitting %s to access '. 57 - 'your account.', 61 + 'This will create an authorization and OAuth token, permitting %s '. 62 + 'to access your account.', 58 63 phutil_tag('strong', array(), $client->getName()))) 59 - ->addCancelButton($view_uri) 64 + ->addCancelButton($done_uri) 60 65 ->addSubmitButton(pht('Authorize Application')); 61 66 } 62 67 }
+22 -12
src/applications/oauthserver/controller/client/PhabricatorOAuthClientViewController.php
··· 51 51 ->setHeader(pht('OAuth Application: %s', $client->getName())) 52 52 ->setPolicyObject($client); 53 53 54 + if ($client->getIsDisabled()) { 55 + $header->setStatus('fa-ban', 'indigo', pht('Disabled')); 56 + } else { 57 + $header->setStatus('fa-check', 'green', pht('Enabled')); 58 + } 59 + 54 60 return $header; 55 61 } 56 62 ··· 62 68 $client, 63 69 PhabricatorPolicyCapability::CAN_EDIT); 64 70 65 - $authorization = id(new PhabricatorOAuthClientAuthorizationQuery()) 66 - ->setViewer($viewer) 67 - ->withUserPHIDs(array($viewer->getPHID())) 68 - ->withClientPHIDs(array($client->getPHID())) 69 - ->executeOne(); 70 - $is_authorized = (bool)$authorization; 71 71 $id = $client->getID(); 72 72 73 73 $view = id(new PhabricatorActionListView()) ··· 89 89 ->setDisabled(!$can_edit) 90 90 ->setWorkflow(true)); 91 91 92 + $is_disabled = $client->getIsDisabled(); 93 + if ($is_disabled) { 94 + $disable_text = pht('Enable Application'); 95 + $disable_icon = 'fa-check'; 96 + } else { 97 + $disable_text = pht('Disable Application'); 98 + $disable_icon = 'fa-ban'; 99 + } 100 + 101 + $disable_uri = $this->getApplicationURI("client/disable/{$id}/"); 102 + 92 103 $view->addAction( 93 104 id(new PhabricatorActionView()) 94 - ->setName(pht('Delete Application')) 95 - ->setIcon('fa-times') 105 + ->setName($disable_text) 106 + ->setIcon($disable_icon) 96 107 ->setWorkflow(true) 97 108 ->setDisabled(!$can_edit) 98 - ->setHref($client->getDeleteURI())); 109 + ->setHref($disable_uri)); 99 110 100 111 $view->addAction( 101 112 id(new PhabricatorActionView()) 102 - ->setName(pht('Create Test Authorization')) 103 - ->setIcon('fa-wrench') 113 + ->setName(pht('Generate Test Token')) 114 + ->setIcon('fa-plus') 104 115 ->setWorkflow(true) 105 - ->setDisabled($is_authorized) 106 116 ->setHref($this->getApplicationURI("client/test/{$id}/"))); 107 117 108 118 return $view;
+9
src/applications/oauthserver/editor/PhabricatorOAuthServerEditor.php
··· 16 16 17 17 $types[] = PhabricatorOAuthServerTransaction::TYPE_NAME; 18 18 $types[] = PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI; 19 + $types[] = PhabricatorOAuthServerTransaction::TYPE_DISABLED; 19 20 20 21 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 21 22 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; ··· 32 33 return $object->getName(); 33 34 case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: 34 35 return $object->getRedirectURI(); 36 + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: 37 + return $object->getIsDisabled(); 35 38 } 36 39 } 37 40 ··· 43 46 case PhabricatorOAuthServerTransaction::TYPE_NAME: 44 47 case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: 45 48 return $xaction->getNewValue(); 49 + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: 50 + return (int)$xaction->getNewValue(); 46 51 } 47 52 } 48 53 ··· 57 62 case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: 58 63 $object->setRedirectURI($xaction->getNewValue()); 59 64 return; 65 + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: 66 + $object->setIsDisabled($xaction->getNewValue()); 67 + return; 60 68 } 61 69 62 70 return parent::applyCustomInternalTransaction($object, $xaction); ··· 69 77 switch ($xaction->getTransactionType()) { 70 78 case PhabricatorOAuthServerTransaction::TYPE_NAME: 71 79 case PhabricatorOAuthServerTransaction::TYPE_REDIRECT_URI: 80 + case PhabricatorOAuthServerTransaction::TYPE_DISABLED: 72 81 return; 73 82 } 74 83
+5 -1
src/applications/oauthserver/query/PhabricatorOAuthServerClientSearchEngine.php
··· 73 73 array $clients, 74 74 PhabricatorSavedQuery $query, 75 75 array $handles) { 76 - assert_instances_of($clients, 'PhabricatorOauthServerClient'); 76 + assert_instances_of($clients, 'PhabricatorOAuthServerClient'); 77 77 78 78 $viewer = $this->requireViewer(); 79 79 ··· 85 85 ->setHeader($client->getName()) 86 86 ->setHref($client->getViewURI()) 87 87 ->setObject($client); 88 + 89 + if ($client->getIsDisabled()) { 90 + $item->setDisabled(true); 91 + } 88 92 89 93 $list->addItem($item); 90 94 }
+6 -12
src/applications/oauthserver/storage/PhabricatorOAuthServerClient.php
··· 11 11 protected $name; 12 12 protected $redirectURI; 13 13 protected $creatorPHID; 14 - protected $isTrusted = 0; 14 + protected $isTrusted; 15 15 protected $viewPolicy; 16 16 protected $editPolicy; 17 + protected $isDisabled; 17 18 18 19 public function getEditURI() { 19 20 $id = $this->getID(); ··· 25 26 return "/oauthserver/client/view/{$id}/"; 26 27 } 27 28 28 - public function getDeleteURI() { 29 - $id = $this->getID(); 30 - return "/oauthserver/client/delete/{$id}/"; 31 - } 32 - 33 29 public static function initializeNewClient(PhabricatorUser $actor) { 34 30 return id(new PhabricatorOAuthServerClient()) 35 31 ->setCreatorPHID($actor->getPHID()) 36 32 ->setSecret(Filesystem::readRandomCharacters(32)) 37 33 ->setViewPolicy(PhabricatorPolicies::POLICY_USER) 38 - ->setEditPolicy($actor->getPHID()); 34 + ->setEditPolicy($actor->getPHID()) 35 + ->setIsDisabled(0) 36 + ->setIsTrusted(0); 39 37 } 40 38 41 39 protected function getConfiguration() { ··· 46 44 'secret' => 'text32', 47 45 'redirectURI' => 'text255', 48 46 'isTrusted' => 'bool', 47 + 'isDisabled' => 'bool', 49 48 ), 50 49 self::CONFIG_KEY_SCHEMA => array( 51 - 'key_phid' => null, 52 - 'phid' => array( 53 - 'columns' => array('phid'), 54 - 'unique' => true, 55 - ), 56 50 'creatorPHID' => array( 57 51 'columns' => array('creatorPHID'), 58 52 ),
+11
src/applications/oauthserver/storage/PhabricatorOAuthServerTransaction.php
··· 5 5 6 6 const TYPE_NAME = 'oauthserver.name'; 7 7 const TYPE_REDIRECT_URI = 'oauthserver.redirect-uri'; 8 + const TYPE_DISABLED = 'oauthserver.disabled'; 8 9 9 10 public function getApplicationName() { 10 11 return 'oauth_server'; ··· 44 45 $this->renderHandleLink($author_phid), 45 46 $old, 46 47 $new); 48 + case self::TYPE_DISABLED: 49 + if ($new) { 50 + return pht( 51 + '%s disabled this application.', 52 + $this->renderHandleLink($author_phid)); 53 + } else { 54 + return pht( 55 + '%s enabled this application.', 56 + $this->renderHandleLink($author_phid)); 57 + } 47 58 } 48 59 49 60 return parent::getTitle();