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

Use transactions to apply web UI SSH key edits

Summary:
Ref T10917. Converts web UI edits to transactions.

This is about 95% "the right way", and then I cheated on the last 5% instead of building a real EditEngine. We don't need it for anything else right now and some of the dialog workflows here are a little weird so I'm just planning to skip it for the moment unless it ends up being easier to do after the next phase (mail notifications) or something like that.

Test Plan: {F1652160}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10917

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

+344 -47
+19
resources/sql/autopatches/20160519.ssh.01.xaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_auth.auth_sshkeytransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+7
src/__phutil_library_map__.php
··· 1878 1878 'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php', 1879 1879 'PhabricatorAuthSSHKeyDeactivateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php', 1880 1880 'PhabricatorAuthSSHKeyEditController' => 'applications/auth/controller/PhabricatorAuthSSHKeyEditController.php', 1881 + 'PhabricatorAuthSSHKeyEditor' => 'applications/auth/editor/PhabricatorAuthSSHKeyEditor.php', 1881 1882 'PhabricatorAuthSSHKeyGenerateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyGenerateController.php', 1882 1883 'PhabricatorAuthSSHKeyListController' => 'applications/auth/controller/PhabricatorAuthSSHKeyListController.php', 1883 1884 'PhabricatorAuthSSHKeyPHIDType' => 'applications/auth/phid/PhabricatorAuthSSHKeyPHIDType.php', 1884 1885 'PhabricatorAuthSSHKeyQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyQuery.php', 1885 1886 'PhabricatorAuthSSHKeySearchEngine' => 'applications/auth/query/PhabricatorAuthSSHKeySearchEngine.php', 1886 1887 'PhabricatorAuthSSHKeyTableView' => 'applications/auth/view/PhabricatorAuthSSHKeyTableView.php', 1888 + 'PhabricatorAuthSSHKeyTransaction' => 'applications/auth/storage/PhabricatorAuthSSHKeyTransaction.php', 1889 + 'PhabricatorAuthSSHKeyTransactionQuery' => 'applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php', 1887 1890 'PhabricatorAuthSSHKeyViewController' => 'applications/auth/controller/PhabricatorAuthSSHKeyViewController.php', 1888 1891 'PhabricatorAuthSSHPublicKey' => 'applications/auth/sshkey/PhabricatorAuthSSHPublicKey.php', 1889 1892 'PhabricatorAuthSession' => 'applications/auth/storage/PhabricatorAuthSession.php', ··· 6305 6308 'PhabricatorAuthDAO', 6306 6309 'PhabricatorPolicyInterface', 6307 6310 'PhabricatorDestructibleInterface', 6311 + 'PhabricatorApplicationTransactionInterface', 6308 6312 ), 6309 6313 'PhabricatorAuthSSHKeyController' => 'PhabricatorAuthController', 6310 6314 'PhabricatorAuthSSHKeyDeactivateController' => 'PhabricatorAuthSSHKeyController', 6311 6315 'PhabricatorAuthSSHKeyEditController' => 'PhabricatorAuthSSHKeyController', 6316 + 'PhabricatorAuthSSHKeyEditor' => 'PhabricatorApplicationTransactionEditor', 6312 6317 'PhabricatorAuthSSHKeyGenerateController' => 'PhabricatorAuthSSHKeyController', 6313 6318 'PhabricatorAuthSSHKeyListController' => 'PhabricatorAuthSSHKeyController', 6314 6319 'PhabricatorAuthSSHKeyPHIDType' => 'PhabricatorPHIDType', 6315 6320 'PhabricatorAuthSSHKeyQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6316 6321 'PhabricatorAuthSSHKeySearchEngine' => 'PhabricatorApplicationSearchEngine', 6317 6322 'PhabricatorAuthSSHKeyTableView' => 'AphrontView', 6323 + 'PhabricatorAuthSSHKeyTransaction' => 'PhabricatorApplicationTransaction', 6324 + 'PhabricatorAuthSSHKeyTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6318 6325 'PhabricatorAuthSSHKeyViewController' => 'PhabricatorAuthSSHKeyController', 6319 6326 'PhabricatorAuthSSHPublicKey' => 'Phobject', 6320 6327 'PhabricatorAuthSession' => array(
+11 -3
src/applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php
··· 27 27 $cancel_uri); 28 28 29 29 if ($request->isFormPost()) { 30 + $xactions = array(); 30 31 31 - // TODO: Convert to transactions. 32 - $key->setIsActive(null); 33 - $key->save(); 32 + $xactions[] = id(new PhabricatorAuthSSHKeyTransaction()) 33 + ->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE) 34 + ->setNewValue(true); 35 + 36 + id(new PhabricatorAuthSSHKeyEditor()) 37 + ->setActor($viewer) 38 + ->setContentSourceFromRequest($request) 39 + ->setContinueOnNoEffect(true) 40 + ->setContinueOnMissingFields(true) 41 + ->applyTransactions($key, $xactions); 34 42 35 43 return id(new AphrontRedirectResponse())->setURI($cancel_uri); 36 44 }
+31 -37
src/applications/auth/controller/PhabricatorAuthSSHKeyEditController.php
··· 59 59 $v_key = $key->getEntireKey(); 60 60 $e_key = strlen($v_key) ? null : true; 61 61 62 - $errors = array(); 62 + $validation_exception = null; 63 63 if ($request->isFormPost()) { 64 + $type_create = PhabricatorTransactions::TYPE_CREATE; 65 + $type_name = PhabricatorAuthSSHKeyTransaction::TYPE_NAME; 66 + $type_key = PhabricatorAuthSSHKeyTransaction::TYPE_KEY; 67 + 68 + $e_name = null; 69 + $e_key = null; 70 + 64 71 $v_name = $request->getStr('name'); 65 72 $v_key = $request->getStr('key'); 66 73 67 - if (!strlen($v_name)) { 68 - $errors[] = pht('You must provide a name for this public key.'); 69 - $e_name = pht('Required'); 70 - } else { 71 - $key->setName($v_name); 72 - } 74 + $xactions = array(); 73 75 74 - if (!strlen($v_key)) { 75 - $errors[] = pht('You must provide a public key.'); 76 - $e_key = pht('Required'); 77 - } else { 78 - try { 79 - $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($v_key); 76 + if (!$key->getID()) { 77 + $xactions[] = id(new PhabricatorAuthSSHKeyTransaction()) 78 + ->setTransactionType(PhabricatorTransactions::TYPE_CREATE); 79 + } 80 80 81 - $type = $public_key->getType(); 82 - $body = $public_key->getBody(); 83 - $comment = $public_key->getComment(); 81 + $xactions[] = id(new PhabricatorAuthSSHKeyTransaction()) 82 + ->setTransactionType($type_name) 83 + ->setNewValue($v_name); 84 84 85 - $key->setKeyType($type); 86 - $key->setKeyBody($body); 87 - $key->setKeyComment($comment); 85 + $xactions[] = id(new PhabricatorAuthSSHKeyTransaction()) 86 + ->setTransactionType($type_key) 87 + ->setNewValue($v_key); 88 88 89 - $e_key = null; 90 - } catch (Exception $ex) { 91 - $e_key = pht('Invalid'); 92 - $errors[] = $ex->getMessage(); 93 - } 94 - } 89 + $editor = id(new PhabricatorAuthSSHKeyEditor()) 90 + ->setActor($viewer) 91 + ->setContentSourceFromRequest($request) 92 + ->setContinueOnNoEffect(true); 95 93 96 - if (!$errors) { 97 - try { 98 - $key->save(); 99 - return id(new AphrontRedirectResponse())->setURI($key->getURI()); 100 - } catch (Exception $ex) { 101 - $e_key = pht('Duplicate'); 102 - $errors[] = pht( 103 - 'This public key is already associated with another user or '. 104 - 'device. Each key must unambiguously identify a single unique '. 105 - 'owner.'); 106 - } 94 + try { 95 + $editor->applyTransactions($key, $xactions); 96 + return id(new AphrontRedirectResponse())->setURI($key->getURI()); 97 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 98 + $validation_exception = $ex; 99 + $e_name = $ex->getShortMessage($type_name); 100 + $e_key = $ex->getShortMessage($type_key); 107 101 } 108 102 } 109 103 ··· 134 128 return $this->newDialog() 135 129 ->setTitle($title) 136 130 ->setWidth(AphrontDialogView::WIDTH_FORM) 137 - ->setErrors($errors) 131 + ->setValidationException($validation_exception) 138 132 ->appendForm($form) 139 133 ->addSubmitButton($save_button) 140 134 ->addCancelButton($cancel_uri);
+7 -6
src/applications/auth/controller/PhabricatorAuthSSHKeyViewController.php
··· 49 49 $crumbs->addTextCrumb($title); 50 50 $crumbs->setBorder(true); 51 51 52 - // TODO: This doesn't exist yet, build it. 53 - // $timeline = $this->buildTransactionTimeline( 54 - // $ssh_key, 55 - // new PhabricatorAuthSSHKeyTransactionQuery()); 56 - // $timeline->setShouldTerminate(true); 57 - $timeline = null; 52 + $timeline = $this->buildTransactionTimeline( 53 + $ssh_key, 54 + new PhabricatorAuthSSHKeyTransactionQuery()); 55 + $timeline->setShouldTerminate(true); 58 56 59 57 $view = id(new PHUITwoColumnView()) 60 58 ->setHeader($header) ··· 113 111 ->setUser($viewer); 114 112 115 113 $properties->addProperty(pht('SSH Key Type'), $ssh_key->getKeyType()); 114 + $properties->addProperty( 115 + pht('Created'), 116 + phabricator_datetime($ssh_key->getDateCreated(), $viewer)); 116 117 117 118 return id(new PHUIObjectBoxView()) 118 119 ->setHeaderText(pht('Details'))
+180
src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthSSHKeyEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorAuthApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('SSH Keys'); 12 + } 13 + 14 + public function getTransactionTypes() { 15 + $types = parent::getTransactionTypes(); 16 + 17 + $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_NAME; 18 + $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_KEY; 19 + $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE; 20 + 21 + return $types; 22 + } 23 + 24 + protected function getCustomTransactionOldValue( 25 + PhabricatorLiskDAO $object, 26 + PhabricatorApplicationTransaction $xaction) { 27 + 28 + switch ($xaction->getTransactionType()) { 29 + case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: 30 + return $object->getName(); 31 + case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: 32 + return $object->getEntireKey(); 33 + case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: 34 + return !$object->getIsActive(); 35 + } 36 + 37 + } 38 + 39 + protected function getCustomTransactionNewValue( 40 + PhabricatorLiskDAO $object, 41 + PhabricatorApplicationTransaction $xaction) { 42 + 43 + switch ($xaction->getTransactionType()) { 44 + case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: 45 + case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: 46 + return $xaction->getNewValue(); 47 + case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: 48 + return (bool)$xaction->getNewValue(); 49 + } 50 + } 51 + 52 + protected function applyCustomInternalTransaction( 53 + PhabricatorLiskDAO $object, 54 + PhabricatorApplicationTransaction $xaction) { 55 + 56 + $value = $xaction->getNewValue(); 57 + switch ($xaction->getTransactionType()) { 58 + case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: 59 + $object->setName($value); 60 + return; 61 + case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: 62 + $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($value); 63 + 64 + $type = $public_key->getType(); 65 + $body = $public_key->getBody(); 66 + $comment = $public_key->getComment(); 67 + 68 + $object->setKeyType($type); 69 + $object->setKeyBody($body); 70 + $object->setKeyComment($comment); 71 + return; 72 + case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: 73 + if ($value) { 74 + $new = null; 75 + } else { 76 + $new = 1; 77 + } 78 + 79 + $object->setIsActive($new); 80 + return; 81 + } 82 + } 83 + 84 + protected function applyCustomExternalTransaction( 85 + PhabricatorLiskDAO $object, 86 + PhabricatorApplicationTransaction $xaction) { 87 + return; 88 + } 89 + 90 + protected function validateTransaction( 91 + PhabricatorLiskDAO $object, 92 + $type, 93 + array $xactions) { 94 + 95 + $errors = parent::validateTransaction($object, $type, $xactions); 96 + 97 + switch ($type) { 98 + case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: 99 + $missing = $this->validateIsEmptyTextField( 100 + $object->getName(), 101 + $xactions); 102 + 103 + if ($missing) { 104 + $error = new PhabricatorApplicationTransactionValidationError( 105 + $type, 106 + pht('Required'), 107 + pht('SSH key name is required.'), 108 + nonempty(last($xactions), null)); 109 + 110 + $error->setIsMissingFieldError(true); 111 + $errors[] = $error; 112 + } 113 + break; 114 + 115 + case PhabricatorAuthSSHKeyTransaction::TYPE_KEY; 116 + $missing = $this->validateIsEmptyTextField( 117 + $object->getName(), 118 + $xactions); 119 + 120 + if ($missing) { 121 + $error = new PhabricatorApplicationTransactionValidationError( 122 + $type, 123 + pht('Required'), 124 + pht('SSH key material is required.'), 125 + nonempty(last($xactions), null)); 126 + 127 + $error->setIsMissingFieldError(true); 128 + $errors[] = $error; 129 + } else { 130 + foreach ($xactions as $xaction) { 131 + $new = $xaction->getNewValue(); 132 + 133 + try { 134 + $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new); 135 + } catch (Exception $ex) { 136 + $errors[] = new PhabricatorApplicationTransactionValidationError( 137 + $type, 138 + pht('Invalid'), 139 + $ex->getMessage(), 140 + $xaction); 141 + } 142 + } 143 + } 144 + break; 145 + 146 + case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: 147 + foreach ($xactions as $xaction) { 148 + if (!$xaction->getNewValue()) { 149 + $errors[] = new PhabricatorApplicationTransactionValidationError( 150 + $type, 151 + pht('Invalid'), 152 + pht('SSH keys can not be reactivated.'), 153 + $xaction); 154 + } 155 + } 156 + break; 157 + } 158 + 159 + return $errors; 160 + } 161 + 162 + protected function didCatchDuplicateKeyException( 163 + PhabricatorLiskDAO $object, 164 + array $xactions, 165 + Exception $ex) { 166 + 167 + $errors = array(); 168 + $errors[] = new PhabricatorApplicationTransactionValidationError( 169 + PhabricatorAuthSSHKeyTransaction::TYPE_KEY, 170 + pht('Duplicate'), 171 + pht( 172 + 'This public key is already associated with another user or device. '. 173 + 'Each key must unambiguously identify a single unique owner.'), 174 + null); 175 + 176 + throw new PhabricatorApplicationTransactionValidationException($errors); 177 + } 178 + 179 + 180 + }
+10
src/applications/auth/query/PhabricatorAuthSSHKeyTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthSSHKeyTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new PhabricatorAuthSSHKeyTransaction(); 8 + } 9 + 10 + }
+24 -1
src/applications/auth/storage/PhabricatorAuthSSHKey.php
··· 4 4 extends PhabricatorAuthDAO 5 5 implements 6 6 PhabricatorPolicyInterface, 7 - PhabricatorDestructibleInterface { 7 + PhabricatorDestructibleInterface, 8 + PhabricatorApplicationTransactionInterface { 8 9 9 10 protected $objectPHID; 10 11 protected $name; ··· 148 149 $this->openTransaction(); 149 150 $this->delete(); 150 151 $this->saveTransaction(); 152 + } 153 + 154 + 155 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 156 + 157 + 158 + public function getApplicationTransactionEditor() { 159 + return new PhabricatorAuthSSHKeyEditor(); 160 + } 161 + 162 + public function getApplicationTransactionObject() { 163 + return $this; 164 + } 165 + 166 + public function getApplicationTransactionTemplate() { 167 + return new PhabricatorAuthProviderConfigTransaction(); 168 + } 169 + 170 + public function willRenderTimeline( 171 + PhabricatorApplicationTransactionView $timeline, 172 + AphrontRequest $request) { 173 + return $timeline; 151 174 } 152 175 153 176 }
+55
src/applications/auth/storage/PhabricatorAuthSSHKeyTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthSSHKeyTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_NAME = 'sshkey.name'; 7 + const TYPE_KEY = 'sshkey.key'; 8 + const TYPE_DEACTIVATE = 'sshkey.deactivate'; 9 + 10 + public function getApplicationName() { 11 + return 'auth'; 12 + } 13 + 14 + public function getApplicationTransactionType() { 15 + return PhabricatorAuthSSHKeyPHIDType::TYPECONST; 16 + } 17 + 18 + public function getApplicationTransactionCommentObject() { 19 + return null; 20 + } 21 + 22 + public function getTitle() { 23 + $author_phid = $this->getAuthorPHID(); 24 + 25 + $old = $this->getOldValue(); 26 + $new = $this->getNewValue(); 27 + 28 + switch ($this->getTransactionType()) { 29 + case self::TYPE_NAME: 30 + return pht( 31 + '%s renamed this key from "%s" to "%s".', 32 + $this->renderHandleLink($author_phid), 33 + $old, 34 + $new); 35 + case self::TYPE_KEY: 36 + return pht( 37 + '%s updated the public key material for this SSH key.', 38 + $this->renderHandleLink($author_phid)); 39 + case self::TYPE_DEACTIVATE: 40 + if ($new) { 41 + return pht( 42 + '%s deactivated this key.', 43 + $this->renderHandleLink($author_phid)); 44 + } else { 45 + return pht( 46 + '%s activated this key.', 47 + $this->renderHandleLink($author_phid)); 48 + } 49 + 50 + } 51 + 52 + return parent::getTitle(); 53 + } 54 + 55 + }