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

Implement `passphrase.query` for querying credentials

Summary: Resolves T5868. This implements `passphrase.query` and a mechanism for allowing Conduit access to credentials.

Test Plan: Tested locally.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: talshiri, epriestley, Korvin

Maniphest Tasks: T5868

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

+274
+2
resources/sql/autopatches/20140814.passphrasecredentialconduit.sql
··· 1 + ALTER TABLE {$NAMESPACE}_passphrase.passphrase_credential 2 + ADD COLUMN allowConduit BOOL NOT NULL DEFAULT 0;
+6
src/__phutil_library_map__.php
··· 1034 1034 'PackageMail' => 'applications/owners/mail/PackageMail.php', 1035 1035 'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php', 1036 1036 'PassphraseAbstractKey' => 'applications/passphrase/keys/PassphraseAbstractKey.php', 1037 + 'PassphraseConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseConduitAPIMethod.php', 1037 1038 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', 1038 1039 'PassphraseCredential' => 'applications/passphrase/storage/PassphraseCredential.php', 1040 + 'PassphraseCredentialConduitController' => 'applications/passphrase/controller/PassphraseCredentialConduitController.php', 1039 1041 'PassphraseCredentialControl' => 'applications/passphrase/view/PassphraseCredentialControl.php', 1040 1042 'PassphraseCredentialCreateController' => 'applications/passphrase/controller/PassphraseCredentialCreateController.php', 1041 1043 'PassphraseCredentialDestroyController' => 'applications/passphrase/controller/PassphraseCredentialDestroyController.php', ··· 1059 1061 'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php', 1060 1062 'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php', 1061 1063 'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php', 1064 + 'PassphraseQueryConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php', 1062 1065 'PassphraseRemarkupRule' => 'applications/passphrase/remarkup/PassphraseRemarkupRule.php', 1063 1066 'PassphraseSSHKey' => 'applications/passphrase/keys/PassphraseSSHKey.php', 1064 1067 'PassphraseSecret' => 'applications/passphrase/storage/PassphraseSecret.php', ··· 3839 3842 'PackageMail' => 'PhabricatorMail', 3840 3843 'PackageModifyMail' => 'PackageMail', 3841 3844 'PassphraseAbstractKey' => 'Phobject', 3845 + 'PassphraseConduitAPIMethod' => 'ConduitAPIMethod', 3842 3846 'PassphraseController' => 'PhabricatorController', 3843 3847 'PassphraseCredential' => array( 3844 3848 'PassphraseDAO', 3845 3849 'PhabricatorPolicyInterface', 3846 3850 'PhabricatorDestructibleInterface', 3847 3851 ), 3852 + 'PassphraseCredentialConduitController' => 'PassphraseController', 3848 3853 'PassphraseCredentialControl' => 'AphrontFormControl', 3849 3854 'PassphraseCredentialCreateController' => 'PassphraseController', 3850 3855 'PassphraseCredentialDestroyController' => 'PassphraseController', ··· 3868 3873 'PassphraseCredentialViewController' => 'PassphraseController', 3869 3874 'PassphraseDAO' => 'PhabricatorLiskDAO', 3870 3875 'PassphrasePasswordKey' => 'PassphraseAbstractKey', 3876 + 'PassphraseQueryConduitAPIMethod' => 'PassphraseConduitAPIMethod', 3871 3877 'PassphraseRemarkupRule' => 'PhabricatorObjectRemarkupRule', 3872 3878 'PassphraseSSHKey' => 'PassphraseAbstractKey', 3873 3879 'PassphraseSecret' => 'PassphraseDAO',
+1
src/applications/passphrase/application/PhabricatorPassphraseApplication.php
··· 46 46 'reveal/(?P<id>\d+)/' => 'PassphraseCredentialRevealController', 47 47 'public/(?P<id>\d+)/' => 'PassphraseCredentialPublicController', 48 48 'lock/(?P<id>\d+)/' => 'PassphraseCredentialLockController', 49 + 'conduit/(?P<id>\d+)/' => 'PassphraseCredentialConduitController', 49 50 )); 50 51 } 51 52
+10
src/applications/passphrase/conduit/PassphraseConduitAPIMethod.php
··· 1 + <?php 2 + 3 + abstract class PassphraseConduitAPIMethod extends ConduitAPIMethod { 4 + 5 + final public function getApplication() { 6 + return PhabricatorApplication::getByClass( 7 + 'PhabricatorPassphraseApplication'); 8 + } 9 + 10 + }
+123
src/applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class PassphraseQueryConduitAPIMethod 4 + extends PassphraseConduitAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'passphrase.query'; 8 + } 9 + 10 + public function getMethodDescription() { 11 + return pht('Query credentials.'); 12 + } 13 + 14 + public function defineParamTypes() { 15 + return array( 16 + 'ids' => 'optional list<int>', 17 + 'phids' => 'optional list<phid>', 18 + 'needSecrets' => 'optional bool', 19 + 'needPublicKeys' => 'optional bool', 20 + ) + $this->getPagerParamTypes(); 21 + } 22 + 23 + public function defineReturnType() { 24 + return 'list<dict>'; 25 + } 26 + 27 + public function defineErrorTypes() { 28 + return array(); 29 + } 30 + 31 + protected function execute(ConduitAPIRequest $request) { 32 + $query = id(new PassphraseCredentialQuery()) 33 + ->setViewer($request->getUser()); 34 + 35 + if ($request->getValue('ids')) { 36 + $query->withIDs($request->getValue('ids')); 37 + } 38 + 39 + if ($request->getValue('phids')) { 40 + $query->withPHIDs($request->getValue('phids')); 41 + } 42 + 43 + if ($request->getValue('needSecrets')) { 44 + $query->needSecrets(true); 45 + } 46 + 47 + $pager = $this->newPager($request); 48 + $credentials = $query->executeWithCursorPager($pager); 49 + 50 + $results = array(); 51 + foreach ($credentials as $credential) { 52 + $type = PassphraseCredentialType::getTypeByConstant( 53 + $credential->getCredentialType()); 54 + if (!$type) { 55 + continue; 56 + } 57 + 58 + $public_key = null; 59 + if ($request->getValue('needPublicKeys') && $type->hasPublicKey()) { 60 + $public_key = $type->getPublicKey( 61 + $request->getUser(), 62 + $credential); 63 + } 64 + 65 + $secret = null; 66 + if ($request->getValue('needSecrets')) { 67 + if ($credential->getAllowConduit()) { 68 + $secret = $credential->getSecret()->openEnvelope(); 69 + } 70 + } 71 + 72 + $material = array(); 73 + switch ($credential->getCredentialType()) { 74 + case PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE: 75 + if ($secret) { 76 + $material['file'] = $secret; 77 + } 78 + if ($public_key) { 79 + $material['publicKey'] = $public_key; 80 + } 81 + break; 82 + case PassphraseCredentialTypeSSHPrivateKeyText::CREDENTIAL_TYPE: 83 + if ($secret) { 84 + $material['privateKey'] = $secret; 85 + } 86 + if ($public_key) { 87 + $material['publicKey'] = $public_key; 88 + } 89 + break; 90 + case PassphraseCredentialTypePassword::CREDENTIAL_TYPE: 91 + if ($secret) { 92 + $material['password'] = $secret; 93 + } 94 + break; 95 + } 96 + 97 + if (!$credential->getAllowConduit()) { 98 + $material['noAPIAccess'] = pht( 99 + 'This credential\'s private material '. 100 + 'is not accessible via API calls.'); 101 + } 102 + 103 + $results[$credential->getPHID()] = array( 104 + 'id' => $credential->getID(), 105 + 'phid' => $credential->getPHID(), 106 + 'type' => $credential->getCredentialType(), 107 + 'name' => $credential->getName(), 108 + 'uri' => 109 + PhabricatorEnv::getProductionURI('/'.$credential->getMonogram()), 110 + 'monogram' => $credential->getMonogram(), 111 + 'username' => $credential->getUsername(), 112 + 'material' => $material, 113 + ); 114 + } 115 + 116 + $result = array( 117 + 'data' => $results, 118 + ); 119 + 120 + return $this->addPagerResults($result, $pager); 121 + } 122 + 123 + }
+81
src/applications/passphrase/controller/PassphraseCredentialConduitController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialConduitController 4 + extends PassphraseController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = $data['id']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $viewer = $request->getUser(); 15 + 16 + $credential = id(new PassphraseCredentialQuery()) 17 + ->setViewer($viewer) 18 + ->withIDs(array($this->id)) 19 + ->requireCapabilities( 20 + array( 21 + PhabricatorPolicyCapability::CAN_VIEW, 22 + PhabricatorPolicyCapability::CAN_EDIT, 23 + )) 24 + ->executeOne(); 25 + if (!$credential) { 26 + return new Aphront404Response(); 27 + } 28 + 29 + $view_uri = '/K'.$credential->getID(); 30 + 31 + $token = id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession( 32 + $viewer, 33 + $request, 34 + $view_uri); 35 + 36 + $type = PassphraseCredentialType::getTypeByConstant( 37 + $credential->getCredentialType()); 38 + if (!$type) { 39 + throw new Exception(pht('Credential has invalid type "%s"!', $type)); 40 + } 41 + 42 + if ($request->isFormPost()) { 43 + $xactions = array(); 44 + $xactions[] = id(new PassphraseCredentialTransaction()) 45 + ->setTransactionType(PassphraseCredentialTransaction::TYPE_CONDUIT) 46 + ->setNewValue(!$credential->getAllowConduit()); 47 + 48 + $editor = id(new PassphraseCredentialTransactionEditor()) 49 + ->setActor($viewer) 50 + ->setContinueOnMissingFields(true) 51 + ->setContentSourceFromRequest($request) 52 + ->applyTransactions($credential, $xactions); 53 + 54 + return id(new AphrontRedirectResponse())->setURI($view_uri); 55 + } 56 + 57 + if ($credential->getAllowConduit()) { 58 + return $this->newDialog() 59 + ->setTitle(pht('Prevent Conduit access?')) 60 + ->appendChild( 61 + pht( 62 + 'This credential and its secret will no longer be able '. 63 + 'to be retrieved using the `passphrase.query` method '. 64 + 'in Conduit.')) 65 + ->addSubmitButton(pht('Prevent Conduit Access')) 66 + ->addCancelButton($view_uri); 67 + } else { 68 + return $this->newDialog() 69 + ->setTitle(pht('Allow Conduit access?')) 70 + ->appendChild( 71 + pht( 72 + 'This credential will be able to be retrieved via the Conduit '. 73 + 'API by users who have access to this credential. You should '. 74 + 'only enable this for credentials which need to be accessed '. 75 + 'programmatically (such as from build agents).')) 76 + ->addSubmitButton(pht('Allow Conduit Access')) 77 + ->addCancelButton($view_uri); 78 + } 79 + } 80 + 81 + }
+16
src/applications/passphrase/controller/PassphraseCredentialViewController.php
··· 93 93 $credential_lock_icon = 'fa-unlock'; 94 94 } 95 95 96 + $allow_conduit = $credential->getAllowConduit(); 97 + if ($allow_conduit) { 98 + $credential_conduit_text = pht('Prevent Conduit Access'); 99 + $credential_conduit_icon = 'fa-ban'; 100 + } else { 101 + $credential_conduit_text = pht('Allow Conduit Access'); 102 + $credential_conduit_icon = 'fa-wrench'; 103 + } 104 + 96 105 $actions = id(new PhabricatorActionListView()) 97 106 ->setObjectURI('/K'.$id) 98 107 ->setUser($viewer); ··· 135 144 ->setHref($this->getApplicationURI("public/{$id}/")) 136 145 ->setWorkflow(true)); 137 146 } 147 + 148 + $actions->addAction( 149 + id(new PhabricatorActionView()) 150 + ->setName($credential_conduit_text) 151 + ->setIcon($credential_conduit_icon) 152 + ->setHref($this->getApplicationURI("conduit/{$id}/")) 153 + ->setWorkflow(true)); 138 154 139 155 $actions->addAction( 140 156 id(new PhabricatorActionView())
+9
src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php
··· 24 24 $types[] = PassphraseCredentialTransaction::TYPE_DESTROY; 25 25 $types[] = PassphraseCredentialTransaction::TYPE_LOOKEDATSECRET; 26 26 $types[] = PassphraseCredentialTransaction::TYPE_LOCK; 27 + $types[] = PassphraseCredentialTransaction::TYPE_CONDUIT; 27 28 28 29 return $types; 29 30 } ··· 47 48 return (int)$object->getIsDestroyed(); 48 49 case PassphraseCredentialTransaction::TYPE_LOCK: 49 50 return (int)$object->getIsLocked(); 51 + case PassphraseCredentialTransaction::TYPE_CONDUIT: 52 + return (int)$object->getAllowConduit(); 50 53 case PassphraseCredentialTransaction::TYPE_LOOKEDATSECRET: 51 54 return null; 52 55 } ··· 66 69 return $xaction->getNewValue(); 67 70 case PassphraseCredentialTransaction::TYPE_DESTROY: 68 71 case PassphraseCredentialTransaction::TYPE_LOCK: 72 + return (int)$xaction->getNewValue(); 73 + case PassphraseCredentialTransaction::TYPE_CONDUIT: 69 74 return (int)$xaction->getNewValue(); 70 75 } 71 76 return parent::getCustomTransactionNewValue($object, $xaction); ··· 114 119 case PassphraseCredentialTransaction::TYPE_LOCK: 115 120 $object->setIsLocked((int)$xaction->getNewValue()); 116 121 return; 122 + case PassphraseCredentialTransaction::TYPE_CONDUIT: 123 + $object->setAllowConduit((int)$xaction->getNewValue()); 124 + return; 117 125 } 118 126 119 127 return parent::applyCustomInternalTransaction($object, $xaction); ··· 131 139 case PassphraseCredentialTransaction::TYPE_DESTROY: 132 140 case PassphraseCredentialTransaction::TYPE_LOOKEDATSECRET: 133 141 case PassphraseCredentialTransaction::TYPE_LOCK: 142 + case PassphraseCredentialTransaction::TYPE_CONDUIT: 134 143 case PhabricatorTransactions::TYPE_VIEW_POLICY: 135 144 case PhabricatorTransactions::TYPE_EDIT_POLICY: 136 145 return;
+13
src/applications/passphrase/query/PassphraseCredentialQuery.php
··· 8 8 private $credentialTypes; 9 9 private $providesTypes; 10 10 private $isDestroyed; 11 + private $allowConduit; 11 12 12 13 private $needSecrets; 13 14 ··· 33 34 34 35 public function withIsDestroyed($destroyed) { 35 36 $this->isDestroyed = $destroyed; 37 + return $this; 38 + } 39 + 40 + public function withAllowConduit($allow_conduit) { 41 + $this->allowConduit = $allow_conduit; 36 42 return $this; 37 43 } 38 44 ··· 125 131 $conn_r, 126 132 'isDestroyed = %d', 127 133 (int)$this->isDestroyed); 134 + } 135 + 136 + if ($this->allowConduit !== null) { 137 + $where[] = qsprintf( 138 + $conn_r, 139 + 'allowConduit = %d', 140 + (int)$this->allowConduit); 128 141 } 129 142 130 143 return $this->formatWhereClause($where);
+1
src/applications/passphrase/storage/PassphraseCredential.php
··· 14 14 protected $secretID; 15 15 protected $isDestroyed; 16 16 protected $isLocked = 0; 17 + protected $allowConduit = 0; 17 18 18 19 private $secret = self::ATTACHABLE; 19 20
+12
src/applications/passphrase/storage/PassphraseCredentialTransaction.php
··· 10 10 const TYPE_DESTROY = 'passphrase:destroy'; 11 11 const TYPE_LOOKEDATSECRET = 'passphrase:lookedAtSecret'; 12 12 const TYPE_LOCK = 'passphrase:lock'; 13 + const TYPE_CONDUIT = 'passphrase:conduit'; 13 14 14 15 public function getApplicationName() { 15 16 return 'passphrase'; ··· 91 92 return pht( 92 93 '%s locked this credential.', 93 94 $this->renderHandleLink($author_phid)); 95 + case self::TYPE_CONDUIT: 96 + if ($old) { 97 + return pht( 98 + '%s disallowed Conduit API access to this credential.', 99 + $this->renderHandleLink($author_phid)); 100 + } else { 101 + return pht( 102 + '%s allowed Conduit API access to this credential.', 103 + $this->renderHandleLink($author_phid)); 104 + } 105 + break; 94 106 } 95 107 96 108 return parent::getTitle();