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

Passphrase v0

Summary:
Ref T4122. Implements a credential management application for the uses described in T4122.

@chad, this needs an icon, HA HA HAHA HA BWW HA HA HA

bwahaha

Test Plan: See screenshots.

Reviewers: btrahan, chad

Reviewed By: btrahan

CC: chad, aran

Maniphest Tasks: T4122

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

+1715 -13
+46
resources/sql/patches/20131119.passphrase.sql
··· 1 + CREATE TABLE {$NAMESPACE}_passphrase.passphrase_credential ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + name VARCHAR(255) NOT NULL, 5 + credentialType VARCHAR(64) NOT NULL COLLATE utf8_bin, 6 + providesType VARCHAR(64) NOT NULL COLLATE utf8_bin, 7 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 8 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 9 + description LONGTEXT NOT NULL COLLATE utf8_bin, 10 + username VARCHAR(255) NOT NULL, 11 + secretID INT UNSIGNED, 12 + isDestroyed BOOL NOT NULL, 13 + dateCreated INT UNSIGNED NOT NULL, 14 + dateModified INT UNSIGNED NOT NULL, 15 + 16 + UNIQUE KEY `key_phid` (phid), 17 + KEY `key_type` (credentialType), 18 + KEY `key_provides` (providesType), 19 + UNIQUE KEY `key_secret` (secretID) 20 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 21 + 22 + CREATE TABLE {$NAMESPACE}_passphrase.passphrase_secret ( 23 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 24 + secretData LONGBLOB NOT NULL 25 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 26 + 27 + CREATE TABLE {$NAMESPACE}_passphrase.passphrase_credentialtransaction ( 28 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 29 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 30 + authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 31 + objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 32 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 33 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 34 + commentPHID VARCHAR(64) COLLATE utf8_bin, 35 + commentVersion INT UNSIGNED NOT NULL, 36 + transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin, 37 + oldValue LONGTEXT NOT NULL COLLATE utf8_bin, 38 + newValue LONGTEXT NOT NULL COLLATE utf8_bin, 39 + contentSource LONGTEXT NOT NULL COLLATE utf8_bin, 40 + metadata LONGTEXT NOT NULL COLLATE utf8_bin, 41 + dateCreated INT UNSIGNED NOT NULL, 42 + dateModified INT UNSIGNED NOT NULL, 43 + 44 + UNIQUE KEY `key_phid` (phid), 45 + KEY `key_object` (objectPHID) 46 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+52
src/__phutil_library_map__.php
··· 943 943 'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php', 944 944 'PackageMail' => 'applications/owners/mail/PackageMail.php', 945 945 'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php', 946 + 'PassphraseController' => 'applications/passphrase/controller/PassphraseController.php', 947 + 'PassphraseCredential' => 'applications/passphrase/storage/PassphraseCredential.php', 948 + 'PassphraseCredentialCreateController' => 'applications/passphrase/controller/PassphraseCredentialCreateController.php', 949 + 'PassphraseCredentialDestroyController' => 'applications/passphrase/controller/PassphraseCredentialDestroyController.php', 950 + 'PassphraseCredentialEditController' => 'applications/passphrase/controller/PassphraseCredentialEditController.php', 951 + 'PassphraseCredentialListController' => 'applications/passphrase/controller/PassphraseCredentialListController.php', 952 + 'PassphraseCredentialQuery' => 'applications/passphrase/query/PassphraseCredentialQuery.php', 953 + 'PassphraseCredentialRevealController' => 'applications/passphrase/controller/PassphraseCredentialRevealController.php', 954 + 'PassphraseCredentialSearchEngine' => 'applications/passphrase/query/PassphraseCredentialSearchEngine.php', 955 + 'PassphraseCredentialTransaction' => 'applications/passphrase/storage/PassphraseCredentialTransaction.php', 956 + 'PassphraseCredentialTransactionEditor' => 'applications/passphrase/editor/PassphraseCredentialTransactionEditor.php', 957 + 'PassphraseCredentialTransactionQuery' => 'applications/passphrase/query/PassphraseCredentialTransactionQuery.php', 958 + 'PassphraseCredentialType' => 'applications/passphrase/credentialtype/PassphraseCredentialType.php', 959 + 'PassphraseCredentialTypePassword' => 'applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php', 960 + 'PassphraseCredentialTypeSSHPrivateKey' => 'applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKey.php', 961 + 'PassphraseCredentialTypeSSHPrivateKeyFile' => 'applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKeyFile.php', 962 + 'PassphraseCredentialTypeSSHPrivateKeyText' => 'applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKeyText.php', 963 + 'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php', 964 + 'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php', 965 + 'PassphrasePHIDTypeCredential' => 'applications/passphrase/phid/PassphrasePHIDTypeCredential.php', 966 + 'PassphraseSecret' => 'applications/passphrase/storage/PassphraseSecret.php', 946 967 'PasteCapabilityDefaultView' => 'applications/paste/capability/PasteCapabilityDefaultView.php', 947 968 'PasteCreateMailReceiver' => 'applications/paste/mail/PasteCreateMailReceiver.php', 948 969 'PasteEmbedView' => 'applications/paste/view/PasteEmbedView.php', ··· 998 1019 'PhabricatorApplicationOwners' => 'applications/owners/application/PhabricatorApplicationOwners.php', 999 1020 'PhabricatorApplicationPHIDTypeApplication' => 'applications/meta/phid/PhabricatorApplicationPHIDTypeApplication.php', 1000 1021 'PhabricatorApplicationPHPAST' => 'applications/phpast/application/PhabricatorApplicationPHPAST.php', 1022 + 'PhabricatorApplicationPassphrase' => 'applications/passphrase/application/PhabricatorApplicationPassphrase.php', 1001 1023 'PhabricatorApplicationPaste' => 'applications/paste/application/PhabricatorApplicationPaste.php', 1002 1024 'PhabricatorApplicationPeople' => 'applications/people/application/PhabricatorApplicationPeople.php', 1003 1025 'PhabricatorApplicationPhame' => 'applications/phame/application/PhabricatorApplicationPhame.php', ··· 3299 3321 'PackageDeleteMail' => 'PackageMail', 3300 3322 'PackageMail' => 'PhabricatorMail', 3301 3323 'PackageModifyMail' => 'PackageMail', 3324 + 'PassphraseController' => 'PhabricatorController', 3325 + 'PassphraseCredential' => 3326 + array( 3327 + 0 => 'PassphraseDAO', 3328 + 1 => 'PhabricatorPolicyInterface', 3329 + ), 3330 + 'PassphraseCredentialCreateController' => 'PassphraseController', 3331 + 'PassphraseCredentialDestroyController' => 'PassphraseController', 3332 + 'PassphraseCredentialEditController' => 'PassphraseController', 3333 + 'PassphraseCredentialListController' => 3334 + array( 3335 + 0 => 'PassphraseController', 3336 + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3337 + ), 3338 + 'PassphraseCredentialQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3339 + 'PassphraseCredentialRevealController' => 'PassphraseController', 3340 + 'PassphraseCredentialSearchEngine' => 'PhabricatorApplicationSearchEngine', 3341 + 'PassphraseCredentialTransaction' => 'PhabricatorApplicationTransaction', 3342 + 'PassphraseCredentialTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 3343 + 'PassphraseCredentialTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 3344 + 'PassphraseCredentialType' => 'Phobject', 3345 + 'PassphraseCredentialTypePassword' => 'PassphraseCredentialType', 3346 + 'PassphraseCredentialTypeSSHPrivateKey' => 'PassphraseCredentialType', 3347 + 'PassphraseCredentialTypeSSHPrivateKeyFile' => 'PassphraseCredentialTypeSSHPrivateKey', 3348 + 'PassphraseCredentialTypeSSHPrivateKeyText' => 'PassphraseCredentialTypeSSHPrivateKey', 3349 + 'PassphraseCredentialViewController' => 'PassphraseController', 3350 + 'PassphraseDAO' => 'PhabricatorLiskDAO', 3351 + 'PassphrasePHIDTypeCredential' => 'PhabricatorPHIDType', 3352 + 'PassphraseSecret' => 'PassphraseDAO', 3302 3353 'PasteCapabilityDefaultView' => 'PhabricatorPolicyCapability', 3303 3354 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 3304 3355 'PasteEmbedView' => 'AphrontView', ··· 3353 3404 'PhabricatorApplicationOwners' => 'PhabricatorApplication', 3354 3405 'PhabricatorApplicationPHIDTypeApplication' => 'PhabricatorPHIDType', 3355 3406 'PhabricatorApplicationPHPAST' => 'PhabricatorApplication', 3407 + 'PhabricatorApplicationPassphrase' => 'PhabricatorApplication', 3356 3408 'PhabricatorApplicationPaste' => 'PhabricatorApplication', 3357 3409 'PhabricatorApplicationPeople' => 'PhabricatorApplication', 3358 3410 'PhabricatorApplicationPhame' => 'PhabricatorApplication',
+4 -7
src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php
··· 64 64 65 65 switch ($type) { 66 66 case HarbormasterBuildPlanTransaction::TYPE_NAME: 67 - $missing_name = true; 68 - if (strlen($object->getName()) && empty($xactions)) { 69 - $missing_name = false; 70 - } else if (strlen(last($xactions)->getNewValue())) { 71 - $missing_name = false; 72 - } 67 + $missing = $this->validateIsEmptyTextField( 68 + $object->getName(), 69 + $xactions); 73 70 74 - if ($missing_name) { 71 + if ($missing) { 75 72 $error = new PhabricatorApplicationTransactionValidationError( 76 73 $type, 77 74 pht('Required'),
+46
src/applications/passphrase/application/PhabricatorApplicationPassphrase.php
··· 1 + <?php 2 + 3 + final class PhabricatorApplicationPassphrase extends PhabricatorApplication { 4 + 5 + public function getBaseURI() { 6 + return '/passphrase/'; 7 + } 8 + 9 + public function getShortDescription() { 10 + return pht('Credential Management'); 11 + } 12 + 13 + public function getIconName() { 14 + return 'passphrase'; 15 + } 16 + 17 + public function getTitleGlyph() { 18 + return "\xE2\x97\x88"; 19 + } 20 + 21 + public function getFlavorText() { 22 + return pht('Put your secrets in a lockbox.'); 23 + } 24 + 25 + public function getApplicationGroup() { 26 + return self::GROUP_UTILITIES; 27 + } 28 + 29 + public function isBeta() { 30 + return true; 31 + } 32 + 33 + public function getRoutes() { 34 + return array( 35 + '/K(?P<id>\d+)' => 'PassphraseCredentialViewController', 36 + '/passphrase/' => array( 37 + '(?:query/(?P<queryKey>[^/]+)/)?' 38 + => 'PassphraseCredentialListController', 39 + 'create/' => 'PassphraseCredentialCreateController', 40 + 'edit/(?:(?P<id>\d+)/)?' => 'PassphraseCredentialEditController', 41 + 'destroy/(?P<id>\d+)/' => 'PassphraseCredentialDestroyController', 42 + 'reveal/(?P<id>\d+)/' => 'PassphraseCredentialRevealController', 43 + )); 44 + } 45 + 46 + }
+40
src/applications/passphrase/controller/PassphraseController.php
··· 1 + <?php 2 + 3 + abstract class PassphraseController extends PhabricatorController { 4 + 5 + public function buildSideNavView($for_app = false) { 6 + $user = $this->getRequest()->getUser(); 7 + 8 + $nav = new AphrontSideNavFilterView(); 9 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 10 + 11 + if ($for_app) { 12 + $nav->addFilter('create', pht('Create Credential')); 13 + } 14 + 15 + id(new PassphraseCredentialSearchEngine()) 16 + ->setViewer($user) 17 + ->addNavigationItems($nav->getMenu()); 18 + 19 + $nav->selectFilter(null); 20 + 21 + return $nav; 22 + } 23 + 24 + public function buildApplicationMenu() { 25 + return $this->buildSideNavView(true)->getMenu(); 26 + } 27 + 28 + public function buildApplicationCrumbs() { 29 + $crumbs = parent::buildApplicationCrumbs(); 30 + 31 + $crumbs->addAction( 32 + id(new PHUIListItemView()) 33 + ->setName(pht('Create Credential')) 34 + ->setHref($this->getApplicationURI('create/')) 35 + ->setIcon('create')); 36 + 37 + return $crumbs; 38 + } 39 + 40 + }
+78
src/applications/passphrase/controller/PassphraseCredentialCreateController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialCreateController extends PassphraseController { 4 + 5 + public function processRequest() { 6 + $request = $this->getRequest(); 7 + $viewer = $request->getUser(); 8 + 9 + $types = PassphraseCredentialType::getAllTypes(); 10 + $types = mpull($types, null, 'getCredentialType'); 11 + $types = msort($types, 'getCredentialTypeName'); 12 + 13 + $errors = array(); 14 + $e_type = null; 15 + 16 + if ($request->isFormPost()) { 17 + $type = $request->getStr('type'); 18 + if (empty($types[$type])) { 19 + $errors[] = pht('You must choose a credential type.'); 20 + $e_type = pht('Required'); 21 + } 22 + 23 + if (!$errors) { 24 + $uri = $this->getApplicationURI('edit/?type='.$type); 25 + return id(new AphrontRedirectResponse())->setURI($uri); 26 + } 27 + } 28 + 29 + $error_view = null; 30 + if ($errors) { 31 + $error_view = id(new AphrontErrorView()) 32 + ->setErrors($errors); 33 + } 34 + 35 + $types_control = id(new AphrontFormRadioButtonControl()) 36 + ->setName('type') 37 + ->setLabel(pht('Credential Type')) 38 + ->setError($e_type); 39 + 40 + foreach ($types as $type) { 41 + $types_control->addButton( 42 + $type->getCredentialType(), 43 + $type->getCredentialTypeName(), 44 + $type->getCredentialTypeDescription()); 45 + } 46 + 47 + $form = id(new AphrontFormView()) 48 + ->setUser($viewer) 49 + ->appendChild($types_control) 50 + ->appendChild( 51 + id(new AphrontFormSubmitControl()) 52 + ->setValue(pht('Continue')) 53 + ->addCancelButton($this->getApplicationURI())); 54 + 55 + $title = pht('New Credential'); 56 + 57 + $crumbs = $this->buildApplicationCrumbs(); 58 + $crumbs->addCrumb( 59 + id(new PhabricatorCrumbView()) 60 + ->setName(pht('Create'))); 61 + 62 + $box = id(new PHUIObjectBoxView()) 63 + ->setHeaderText(pht('Create New Credential')) 64 + ->setFormError($error_view) 65 + ->setForm($form); 66 + 67 + return $this->buildApplicationPage( 68 + array( 69 + $crumbs, 70 + $box, 71 + ), 72 + array( 73 + 'title' => $title, 74 + 'device' => true, 75 + )); 76 + } 77 + 78 + }
+67
src/applications/passphrase/controller/PassphraseCredentialDestroyController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialDestroyController 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 + $type = PassphraseCredentialType::getTypeByConstant( 30 + $credential->getCredentialType()); 31 + if (!$type) { 32 + throw new Exception(pht('Credential has invalid type "%s"!', $type)); 33 + } 34 + 35 + $view_uri = '/K'.$credential->getID(); 36 + 37 + if ($request->isFormPost()) { 38 + 39 + $xactions = array(); 40 + $xactions[] = id(new PassphraseCredentialTransaction()) 41 + ->setTransactionType(PassphraseCredentialTransaction::TYPE_DESTROY) 42 + ->setNewValue(1); 43 + 44 + $editor = id(new PassphraseCredentialTransactionEditor()) 45 + ->setActor($viewer) 46 + ->setContinueOnMissingFields(true) 47 + ->setContentSourceFromRequest($request) 48 + ->applyTransactions($credential, $xactions); 49 + 50 + return id(new AphrontRedirectResponse())->setURI($view_uri); 51 + } 52 + 53 + $dialog = id(new AphrontDialogView()) 54 + ->setUser($viewer) 55 + ->setTitle(pht('Really destroy credential?')) 56 + ->appendChild( 57 + pht( 58 + 'This credential will be deactivated and the secret will be '. 59 + 'unrecoverably destroyed. Anything relying on this credential will '. 60 + 'cease to function. This operation can not be undone.')) 61 + ->addSubmitButton(pht('Destroy Credential')) 62 + ->addCancelButton($view_uri); 63 + 64 + return id(new AphrontDialogResponse())->setDialog($dialog); 65 + } 66 + 67 + }
+241
src/applications/passphrase/controller/PassphraseCredentialEditController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialEditController extends PassphraseController { 4 + 5 + private $id; 6 + 7 + public function willProcessRequest(array $data) { 8 + $this->id = idx($data, 'id'); 9 + } 10 + 11 + public function processRequest() { 12 + $request = $this->getRequest(); 13 + $viewer = $request->getUser(); 14 + 15 + if ($this->id) { 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 + $type = PassphraseCredentialType::getTypeByConstant( 30 + $credential->getCredentialType()); 31 + if (!$type) { 32 + throw new Exception(pht('Credential has invalid type "%s"!', $type)); 33 + } 34 + 35 + $is_new = false; 36 + } else { 37 + $type_const = $request->getStr('type'); 38 + $type = PassphraseCredentialType::getTypeByConstant($type_const); 39 + if (!$type) { 40 + return new Aphront404Response(); 41 + } 42 + 43 + $credential = PassphraseCredential::initializeNewCredential($viewer) 44 + ->setCredentialType($type->getCredentialType()) 45 + ->setProvidesType($type->getProvidesType()); 46 + 47 + $is_new = true; 48 + } 49 + 50 + $errors = array(); 51 + 52 + $v_name = $credential->getName(); 53 + $e_name = true; 54 + 55 + $v_desc = $credential->getDescription(); 56 + 57 + $v_username = $credential->getUsername(); 58 + $e_username = true; 59 + 60 + $bullet = "\xE2\x80\xA2"; 61 + 62 + $v_secret = $credential->getSecretID() ? str_repeat($bullet, 32) : null; 63 + 64 + $validation_exception = null; 65 + if ($request->isFormPost()) { 66 + $v_name = $request->getStr('name'); 67 + $v_desc = $request->getStr('description'); 68 + $v_username = $request->getStr('username'); 69 + $v_secret = $request->getStr('secret'); 70 + $v_view_policy = $request->getStr('viewPolicy'); 71 + $v_edit_policy = $request->getStr('editPolicy'); 72 + 73 + $type_name = PassphraseCredentialTransaction::TYPE_NAME; 74 + $type_desc = PassphraseCredentialTransaction::TYPE_DESCRIPTION; 75 + $type_username = PassphraseCredentialTransaction::TYPE_USERNAME; 76 + $type_destroy = PassphraseCredentialTransaction::TYPE_DESTROY; 77 + $type_secret_id = PassphraseCredentialTransaction::TYPE_SECRET_ID; 78 + $type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY; 79 + $type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY; 80 + 81 + $xactions = array(); 82 + 83 + $xactions[] = id(new PassphraseCredentialTransaction()) 84 + ->setTransactionType($type_name) 85 + ->setNewValue($v_name); 86 + 87 + $xactions[] = id(new PassphraseCredentialTransaction()) 88 + ->setTransactionType($type_desc) 89 + ->setNewValue($v_desc); 90 + 91 + $xactions[] = id(new PassphraseCredentialTransaction()) 92 + ->setTransactionType($type_username) 93 + ->setNewValue($v_username); 94 + 95 + $xactions[] = id(new PassphraseCredentialTransaction()) 96 + ->setTransactionType($type_view_policy) 97 + ->setNewValue($v_view_policy); 98 + 99 + $xactions[] = id(new PassphraseCredentialTransaction()) 100 + ->setTransactionType($type_edit_policy) 101 + ->setNewValue($v_edit_policy); 102 + 103 + // Open a transaction in case we're writing a new secret; this limits 104 + // the amount of code which handles secret plaintexts. 105 + $credential->openTransaction(); 106 + 107 + $min_secret = str_replace($bullet, '', trim($v_secret)); 108 + if (strlen($min_secret)) { 109 + // If the credential was previously destroyed, restore it when it is 110 + // edited if a secret is provided. 111 + $xactions[] = id(new PassphraseCredentialTransaction()) 112 + ->setTransactionType($type_destroy) 113 + ->setNewValue(0); 114 + 115 + $new_secret = id(new PassphraseSecret()) 116 + ->setSecretData($v_secret) 117 + ->save(); 118 + $xactions[] = id(new PassphraseCredentialTransaction()) 119 + ->setTransactionType($type_secret_id) 120 + ->setNewValue($new_secret->getID()); 121 + } 122 + 123 + try { 124 + $editor = id(new PassphraseCredentialTransactionEditor()) 125 + ->setActor($viewer) 126 + ->setContinueOnNoEffect(true) 127 + ->setContentSourceFromRequest($request) 128 + ->applyTransactions($credential, $xactions); 129 + 130 + $credential->saveTransaction(); 131 + 132 + return id(new AphrontRedirectResponse()) 133 + ->setURI('/K'.$credential->getID()); 134 + } catch (PhabricatorApplicationTransactionValidationException $ex) { 135 + $credential->killTransaction(); 136 + 137 + $validation_exception = $ex; 138 + 139 + $e_name = $ex->getShortMessage($type_name); 140 + $e_username = $ex->getShortMessage($type_username); 141 + 142 + $credential->setViewPolicy($v_view_policy); 143 + $credential->setEditPolicy($v_edit_policy); 144 + } 145 + } 146 + 147 + $policies = id(new PhabricatorPolicyQuery()) 148 + ->setViewer($viewer) 149 + ->setObject($credential) 150 + ->execute(); 151 + 152 + $secret_control = $type->newSecretControl(); 153 + 154 + $form = id(new AphrontFormView()) 155 + ->setUser($viewer) 156 + ->appendChild( 157 + id(new AphrontFormTextControl()) 158 + ->setName('name') 159 + ->setLabel(pht('Name')) 160 + ->setValue($v_name) 161 + ->setError($e_name)) 162 + ->appendChild( 163 + id(new AphrontFormTextAreaControl()) 164 + ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT) 165 + ->setName('description') 166 + ->setLabel(pht('Description')) 167 + ->setValue($v_desc)) 168 + ->appendChild( 169 + id(new AphrontFormMarkupControl()) 170 + ->setLabel(pht('Credential Type')) 171 + ->setValue($type->getCredentialTypeName())) 172 + ->appendChild( 173 + id(new AphrontFormDividerControl())) 174 + ->appendChild( 175 + id(new AphrontFormPolicyControl()) 176 + ->setName('viewPolicy') 177 + ->setPolicyObject($credential) 178 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 179 + ->setPolicies($policies)) 180 + ->appendChild( 181 + id(new AphrontFormPolicyControl()) 182 + ->setName('editPolicy') 183 + ->setPolicyObject($credential) 184 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 185 + ->setPolicies($policies)) 186 + ->appendChild( 187 + id(new AphrontFormDividerControl())) 188 + ->appendChild( 189 + id(new AphrontFormTextControl()) 190 + ->setName('username') 191 + ->setLabel(pht('Login/Username')) 192 + ->setValue($v_username) 193 + ->setError($e_username)) 194 + ->appendChild( 195 + $secret_control 196 + ->setName('secret') 197 + ->setLabel($type->getSecretLabel()) 198 + ->setValue($v_secret)); 199 + 200 + $form->appendChild( 201 + id(new AphrontFormSubmitControl()) 202 + ->setValue(pht('Save')) 203 + ->addCancelButton($this->getApplicationURI())); 204 + 205 + $crumbs = $this->buildApplicationCrumbs(); 206 + 207 + if ($is_new) { 208 + $title = pht('Create Credential'); 209 + $header = pht('Create New Credential'); 210 + $crumbs->addCrumb( 211 + id(new PhabricatorCrumbView()) 212 + ->setName(pht('Create'))); 213 + } else { 214 + $title = pht('Edit Credential'); 215 + $header = pht('Edit Credential %s', 'K'.$credential->getID()); 216 + $crumbs->addCrumb( 217 + id(new PhabricatorCrumbView()) 218 + ->setName('K'.$credential->getID()) 219 + ->setHref('/K'.$credential->getID())); 220 + $crumbs->addCrumb( 221 + id(new PhabricatorCrumbView()) 222 + ->setName(pht('Edit'))); 223 + } 224 + 225 + $box = id(new PHUIObjectBoxView()) 226 + ->setHeaderText($header) 227 + ->setValidationException($validation_exception) 228 + ->setForm($form); 229 + 230 + return $this->buildApplicationPage( 231 + array( 232 + $crumbs, 233 + $box, 234 + ), 235 + array( 236 + 'title' => $title, 237 + 'device' => true, 238 + )); 239 + } 240 + 241 + }
+63
src/applications/passphrase/controller/PassphraseCredentialListController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialListController extends PassphraseController 4 + implements PhabricatorApplicationSearchResultsControllerInterface { 5 + 6 + private $queryKey; 7 + 8 + public function shouldAllowPublic() { 9 + return true; 10 + } 11 + 12 + public function willProcessRequest(array $data) { 13 + $this->queryKey = idx($data, 'queryKey'); 14 + } 15 + 16 + public function processRequest() { 17 + $request = $this->getRequest(); 18 + $controller = id(new PhabricatorApplicationSearchController($request)) 19 + ->setQueryKey($this->queryKey) 20 + ->setSearchEngine(new PassphraseCredentialSearchEngine()) 21 + ->setNavigation($this->buildSideNavView()); 22 + 23 + return $this->delegateToController($controller); 24 + } 25 + 26 + public function renderResultsList( 27 + array $credentials, 28 + PhabricatorSavedQuery $query) { 29 + assert_instances_of($credentials, 'PassphraseCredential'); 30 + 31 + $viewer = $this->getRequest()->getUser(); 32 + 33 + $list = new PHUIObjectItemListView(); 34 + $list->setUser($viewer); 35 + foreach ($credentials as $credential) { 36 + 37 + $item = id(new PHUIObjectItemView()) 38 + ->setObjectName('K'.$credential->getID()) 39 + ->setHeader($credential->getName()) 40 + ->setHref('/K'.$credential->getID()) 41 + ->setObject($credential); 42 + 43 + $item->addAttribute( 44 + pht('Login: %s', $credential->getUsername())); 45 + 46 + if ($credential->getIsDestroyed()) { 47 + $item->addIcon('disable', pht('Destroyed')); 48 + $item->setDisabled(true); 49 + } 50 + 51 + $type = PassphraseCredentialType::getTypeByConstant( 52 + $credential->getCredentialType()); 53 + if ($type) { 54 + $item->addIcon('wrench', $type->getCredentialTypeName()); 55 + } 56 + 57 + $list->addItem($item); 58 + } 59 + 60 + return $list; 61 + } 62 + 63 + }
+75
src/applications/passphrase/controller/PassphraseCredentialRevealController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialRevealController 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 + ->needSecrets(true) 25 + ->executeOne(); 26 + if (!$credential) { 27 + return new Aphront404Response(); 28 + } 29 + 30 + $view_uri = '/K'.$credential->getID(); 31 + 32 + if ($request->isFormPost()) { 33 + if ($credential->getSecret()) { 34 + $body = id(new PHUIFormLayoutView()) 35 + ->appendChild( 36 + id(new AphrontFormTextAreaControl()) 37 + ->setLabel(pht('Plaintext')) 38 + ->setValue($credential->getSecret()->openEnvelope())); 39 + } else { 40 + $body = pht('This credential has no associated secret.'); 41 + } 42 + 43 + $dialog = id(new AphrontDialogView()) 44 + ->setUser($viewer) 45 + ->setTitle(pht('Credential Secret')) 46 + ->appendChild($body) 47 + ->addCancelButton($view_uri, pht('Done')); 48 + 49 + return id(new AphrontDialogResponse())->setDialog($dialog); 50 + } 51 + 52 + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 53 + if ($is_serious) { 54 + $body = pht( 55 + 'The secret associated with this credential will be shown in plain '. 56 + 'text on your screen.'); 57 + } else { 58 + $body = pht( 59 + 'The secret associated with this credential will be shown in plain '. 60 + 'text on your screen. Before continuing, wrap your arms around your '. 61 + 'monitor to create a human shield, keeping it safe from prying eyes. '. 62 + 'Protect company secrets!'); 63 + } 64 + 65 + $dialog = id(new AphrontDialogView()) 66 + ->setUser($viewer) 67 + ->setTitle(pht('Really show secret?')) 68 + ->appendChild($body) 69 + ->addSubmitButton(pht('Show Secret')) 70 + ->addCancelButton($view_uri); 71 + 72 + return id(new AphrontDialogResponse())->setDialog($dialog); 73 + } 74 + 75 + }
+170
src/applications/passphrase/controller/PassphraseCredentialViewController.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialViewController extends PassphraseController { 4 + 5 + private $id; 6 + 7 + public function willProcessRequest(array $data) { 8 + $this->id = $data['id']; 9 + } 10 + 11 + public function processRequest() { 12 + $request = $this->getRequest(); 13 + $viewer = $request->getUser(); 14 + 15 + $credential = id(new PassphraseCredentialQuery()) 16 + ->setViewer($viewer) 17 + ->withIDs(array($this->id)) 18 + ->executeOne(); 19 + if (!$credential) { 20 + return new Aphront404Response(); 21 + } 22 + 23 + $type = PassphraseCredentialType::getTypeByConstant( 24 + $credential->getCredentialType()); 25 + if (!$type) { 26 + throw new Exception(pht('Credential has invalid type "%s"!', $type)); 27 + } 28 + 29 + $xactions = id(new PassphraseCredentialTransactionQuery()) 30 + ->setViewer($viewer) 31 + ->withObjectPHIDs(array($credential->getPHID())) 32 + ->execute(); 33 + 34 + $engine = id(new PhabricatorMarkupEngine()) 35 + ->setViewer($viewer); 36 + 37 + $timeline = id(new PhabricatorApplicationTransactionView()) 38 + ->setUser($viewer) 39 + ->setObjectPHID($credential->getPHID()) 40 + ->setTransactions($xactions); 41 + 42 + $title = pht('%s %s', 'K'.$credential->getID(), $credential->getName()); 43 + $crumbs = $this->buildApplicationCrumbs(); 44 + $crumbs->addCrumb( 45 + id(new PhabricatorCrumbView()) 46 + ->setName('K'.$credential->getID())); 47 + 48 + $header = $this->buildHeaderView($credential); 49 + $actions = $this->buildActionView($credential); 50 + $properties = $this->buildPropertyView($credential, $type, $actions); 51 + 52 + $box = id(new PHUIObjectBoxView()) 53 + ->setHeader($header) 54 + ->addPropertyList($properties); 55 + 56 + return $this->buildApplicationPage( 57 + array( 58 + $crumbs, 59 + $box, 60 + $timeline, 61 + ), 62 + array( 63 + 'title' => $title, 64 + 'device' => true, 65 + )); 66 + } 67 + 68 + private function buildHeaderView(PassphraseCredential $credential) { 69 + $viewer = $this->getRequest()->getUser(); 70 + 71 + $header = id(new PHUIHeaderView()) 72 + ->setUser($viewer) 73 + ->setHeader($credential->getName()) 74 + ->setPolicyObject($credential); 75 + 76 + if ($credential->getIsDestroyed()) { 77 + $header->setStatus('reject', 'red', pht('Destroyed')); 78 + } 79 + 80 + return $header; 81 + } 82 + 83 + private function buildActionView(PassphraseCredential $credential) { 84 + $viewer = $this->getRequest()->getUser(); 85 + 86 + $id = $credential->getID(); 87 + 88 + $actions = id(new PhabricatorActionListView()) 89 + ->setObjectURI('/K'.$id) 90 + ->setUser($viewer); 91 + 92 + $can_edit = PhabricatorPolicyFilter::hasCapability( 93 + $viewer, 94 + $credential, 95 + PhabricatorPolicyCapability::CAN_EDIT); 96 + 97 + $actions->addAction( 98 + id(new PhabricatorActionView()) 99 + ->setName(pht('Edit Credential')) 100 + ->setIcon('edit') 101 + ->setHref($this->getApplicationURI("edit/{$id}/")) 102 + ->setDisabled(!$can_edit) 103 + ->setWorkflow(!$can_edit)); 104 + 105 + if (!$credential->getIsDestroyed()) { 106 + $actions->addAction( 107 + id(new PhabricatorActionView()) 108 + ->setName(pht('Destroy Credential')) 109 + ->setIcon('delete') 110 + ->setHref($this->getApplicationURI("destroy/{$id}/")) 111 + ->setDisabled(!$can_edit) 112 + ->setWorkflow(true)); 113 + 114 + $actions->addAction( 115 + id(new PhabricatorActionView()) 116 + ->setName(pht('Show Secret')) 117 + ->setIcon('preview') 118 + ->setHref($this->getApplicationURI("reveal/{$id}/")) 119 + ->setDisabled(!$can_edit) 120 + ->setWorkflow(true)); 121 + } 122 + 123 + 124 + return $actions; 125 + } 126 + 127 + private function buildPropertyView( 128 + PassphraseCredential $credential, 129 + PassphraseCredentialType $type, 130 + PhabricatorActionListView $actions) { 131 + $viewer = $this->getRequest()->getUser(); 132 + 133 + $properties = id(new PHUIPropertyListView()) 134 + ->setUser($viewer) 135 + ->setObject($credential) 136 + ->setActionList($actions); 137 + 138 + $properties->addProperty( 139 + pht('Credential Type'), 140 + $type->getCredentialTypeName()); 141 + 142 + $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( 143 + $viewer, 144 + $credential); 145 + 146 + $properties->addProperty( 147 + pht('Editable By'), 148 + $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); 149 + 150 + $properties->addProperty( 151 + pht('Username'), 152 + $credential->getUsername()); 153 + 154 + $description = $credential->getDescription(); 155 + if (strlen($description)) { 156 + $properties->addSectionHeader( 157 + pht('Description'), 158 + PHUIPropertyListView::ICON_SUMMARY); 159 + $properties->addTextContent( 160 + PhabricatorMarkupEngine::renderOneObject( 161 + id(new PhabricatorMarkupOneOff()) 162 + ->setContent($description), 163 + 'default', 164 + $viewer)); 165 + } 166 + 167 + return $properties; 168 + } 169 + 170 + }
+28
src/applications/passphrase/credentialtype/PassphraseCredentialType.php
··· 1 + <?php 2 + 3 + abstract class PassphraseCredentialType extends Phobject { 4 + 5 + abstract public function getCredentialType(); 6 + abstract public function getProvidesType(); 7 + abstract public function getCredentialTypeName(); 8 + abstract public function getCredentialTypeDescription(); 9 + abstract public function getSecretLabel(); 10 + 11 + public function newSecretControl() { 12 + return new AphrontFormTextAreaControl(); 13 + } 14 + 15 + public static function getAllTypes() { 16 + $types = id(new PhutilSymbolLoader()) 17 + ->setAncestorClass(__CLASS__) 18 + ->loadObjects(); 19 + return $types; 20 + } 21 + 22 + public static function getTypeByConstant($constant) { 23 + $all = self::getAllTypes(); 24 + $all = mpull($all, null, 'getCredentialType'); 25 + return idx($all, $constant); 26 + } 27 + 28 + }
+30
src/applications/passphrase/credentialtype/PassphraseCredentialTypePassword.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTypePassword 4 + extends PassphraseCredentialType { 5 + 6 + public function getCredentialType() { 7 + return 'password'; 8 + } 9 + 10 + public function getProvidesType() { 11 + return 'provides/password'; 12 + } 13 + 14 + public function getCredentialTypeName() { 15 + return pht('Password'); 16 + } 17 + 18 + public function getCredentialTypeDescription() { 19 + return pht('Store a plaintext password.'); 20 + } 21 + 22 + public function getSecretLabel() { 23 + return pht('Password'); 24 + } 25 + 26 + public function newSecretControl() { 27 + return new AphrontFormPasswordControl(); 28 + } 29 + 30 + }
+10
src/applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKey.php
··· 1 + <?php 2 + 3 + abstract class PassphraseCredentialTypeSSHPrivateKey 4 + extends PassphraseCredentialType { 5 + 6 + final public function getProvidesType() { 7 + return 'provides/ssh-key-file'; 8 + } 9 + 10 + }
+26
src/applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKeyFile.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTypeSSHPrivateKeyFile 4 + extends PassphraseCredentialTypeSSHPrivateKey { 5 + 6 + public function getCredentialType() { 7 + return 'ssh-key-file'; 8 + } 9 + 10 + public function getCredentialTypeName() { 11 + return pht('SSH Private Key File'); 12 + } 13 + 14 + public function getCredentialTypeDescription() { 15 + return pht('Store the path on disk to an SSH private key.'); 16 + } 17 + 18 + public function getSecretLabel() { 19 + return pht('Path On Disk'); 20 + } 21 + 22 + public function newSecretControl() { 23 + return new AphrontFormTextControl(); 24 + } 25 + 26 + }
+22
src/applications/passphrase/credentialtype/PassphraseCredentialTypeSSHPrivateKeyText.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTypeSSHPrivateKeyText 4 + extends PassphraseCredentialTypeSSHPrivateKey { 5 + 6 + public function getCredentialType() { 7 + return 'ssh-key-text'; 8 + } 9 + 10 + public function getCredentialTypeName() { 11 + return pht('SSH Private Key'); 12 + } 13 + 14 + public function getCredentialTypeDescription() { 15 + return pht('Store the plaintext of an SSH private key.'); 16 + } 17 + 18 + public function getSecretLabel() { 19 + return pht('Private Key'); 20 + } 21 + 22 + }
+164
src/applications/passphrase/editor/PassphraseCredentialTransactionEditor.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTransactionEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getTransactionTypes() { 7 + $types = parent::getTransactionTypes(); 8 + 9 + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 10 + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 11 + 12 + $types[] = PassphraseCredentialTransaction::TYPE_NAME; 13 + $types[] = PassphraseCredentialTransaction::TYPE_DESCRIPTION; 14 + $types[] = PassphraseCredentialTransaction::TYPE_USERNAME; 15 + $types[] = PassphraseCredentialTransaction::TYPE_SECRET_ID; 16 + $types[] = PassphraseCredentialTransaction::TYPE_DESTROY; 17 + 18 + return $types; 19 + } 20 + 21 + protected function getCustomTransactionOldValue( 22 + PhabricatorLiskDAO $object, 23 + PhabricatorApplicationTransaction $xaction) { 24 + switch ($xaction->getTransactionType()) { 25 + case PassphraseCredentialTransaction::TYPE_NAME: 26 + if ($this->getIsNewObject()) { 27 + return null; 28 + } 29 + return $object->getName(); 30 + case PassphraseCredentialTransaction::TYPE_DESCRIPTION: 31 + return $object->getDescription(); 32 + case PassphraseCredentialTransaction::TYPE_USERNAME: 33 + return $object->getUsername(); 34 + case PassphraseCredentialTransaction::TYPE_SECRET_ID: 35 + return $object->getSecretID(); 36 + case PassphraseCredentialTransaction::TYPE_DESTROY: 37 + return $object->getIsDestroyed(); 38 + } 39 + 40 + return parent::getCustomTransactionOldValue($object, $xaction); 41 + } 42 + 43 + protected function getCustomTransactionNewValue( 44 + PhabricatorLiskDAO $object, 45 + PhabricatorApplicationTransaction $xaction) { 46 + switch ($xaction->getTransactionType()) { 47 + case PassphraseCredentialTransaction::TYPE_NAME: 48 + case PassphraseCredentialTransaction::TYPE_DESCRIPTION: 49 + case PassphraseCredentialTransaction::TYPE_USERNAME: 50 + case PassphraseCredentialTransaction::TYPE_SECRET_ID: 51 + case PassphraseCredentialTransaction::TYPE_DESTROY: 52 + return $xaction->getNewValue(); 53 + } 54 + return parent::getCustomTransactionNewValue($object, $xaction); 55 + } 56 + 57 + protected function applyCustomInternalTransaction( 58 + PhabricatorLiskDAO $object, 59 + PhabricatorApplicationTransaction $xaction) { 60 + switch ($xaction->getTransactionType()) { 61 + case PassphraseCredentialTransaction::TYPE_NAME: 62 + $object->setName($xaction->getNewValue()); 63 + return; 64 + case PassphraseCredentialTransaction::TYPE_DESCRIPTION: 65 + $object->setDescription($xaction->getNewValue()); 66 + return; 67 + case PassphraseCredentialTransaction::TYPE_USERNAME: 68 + $object->setUsername($xaction->getNewValue()); 69 + return; 70 + case PassphraseCredentialTransaction::TYPE_SECRET_ID: 71 + $old_id = $object->getSecretID(); 72 + if ($old_id) { 73 + $this->destroySecret($old_id); 74 + } 75 + $object->setSecretID($xaction->getNewValue()); 76 + return; 77 + case PassphraseCredentialTransaction::TYPE_DESTROY: 78 + // When destroying a credential, wipe out its secret. 79 + $is_destroyed = $xaction->getNewValue(); 80 + $object->setIsDestroyed($is_destroyed); 81 + if ($is_destroyed) { 82 + $secret_id = $object->getSecretID(); 83 + if ($secret_id) { 84 + $this->destroySecret($secret_id); 85 + $object->setSecretID(null); 86 + } 87 + } 88 + return; 89 + } 90 + return parent::applyCustomInternalTransaction($object, $xaction); 91 + } 92 + 93 + protected function applyCustomExternalTransaction( 94 + PhabricatorLiskDAO $object, 95 + PhabricatorApplicationTransaction $xaction) { 96 + 97 + switch ($xaction->getTransactionType()) { 98 + case PassphraseCredentialTransaction::TYPE_NAME: 99 + case PassphraseCredentialTransaction::TYPE_DESCRIPTION: 100 + case PassphraseCredentialTransaction::TYPE_USERNAME: 101 + case PassphraseCredentialTransaction::TYPE_SECRET_ID: 102 + case PassphraseCredentialTransaction::TYPE_DESTROY: 103 + return; 104 + } 105 + 106 + return parent::applyCustomExternalTransaction($object, $xaction); 107 + } 108 + 109 + private function destroySecret($secret_id) { 110 + $table = new PassphraseSecret(); 111 + queryfx( 112 + $table->establishConnection('w'), 113 + 'DELETE FROM %T WHERE id = %d', 114 + $table->getTableName(), 115 + $secret_id); 116 + } 117 + 118 + protected function validateTransaction( 119 + PhabricatorLiskDAO $object, 120 + $type, 121 + array $xactions) { 122 + 123 + $errors = parent::validateTransaction($object, $type, $xactions); 124 + 125 + switch ($type) { 126 + case PassphraseCredentialTransaction::TYPE_NAME: 127 + $missing = $this->validateIsEmptyTextField( 128 + $object->getName(), 129 + $xactions); 130 + 131 + if ($missing) { 132 + $error = new PhabricatorApplicationTransactionValidationError( 133 + $type, 134 + pht('Required'), 135 + pht('Credential name is required.'), 136 + nonempty(last($xactions), null)); 137 + 138 + $error->setIsMissingFieldError(true); 139 + $errors[] = $error; 140 + } 141 + break; 142 + case PassphraseCredentialTransaction::TYPE_USERNAME: 143 + $missing = $this->validateIsEmptyTextField( 144 + $object->getUsername(), 145 + $xactions); 146 + 147 + if ($missing) { 148 + $error = new PhabricatorApplicationTransactionValidationError( 149 + $type, 150 + pht('Required'), 151 + pht('Username is required.'), 152 + nonempty(last($xactions), null)); 153 + 154 + $error->setIsMissingFieldError(true); 155 + $errors[] = $error; 156 + } 157 + break; 158 + } 159 + 160 + return $errors; 161 + } 162 + 163 + 164 + }
+76
src/applications/passphrase/phid/PassphrasePHIDTypeCredential.php
··· 1 + <?php 2 + 3 + final class PassphrasePHIDTypeCredential extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'CDTL'; 6 + 7 + public function getTypeConstant() { 8 + return self::TYPECONST; 9 + } 10 + 11 + public function getTypeName() { 12 + return pht('Credential'); 13 + } 14 + 15 + public function newObject() { 16 + return new PassphraseCredential(); 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + 23 + return id(new PassphraseCredentialQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + foreach ($handles as $phid => $handle) { 33 + $credential = $objects[$phid]; 34 + $id = $credential->getID(); 35 + $name = $credential->getName(); 36 + 37 + $handle->setName("K{$id}"); 38 + $handle->setFullName("K{$id} {$name}"); 39 + $handle->setURI("/K{$id}"); 40 + 41 + if ($credential->getIsDestroyed()) { 42 + $handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED); 43 + } 44 + } 45 + } 46 + 47 + public function canLoadNamedObject($name) { 48 + return preg_match('/^K\d*[1-9]\d*$/i', $name); 49 + } 50 + 51 + public function loadNamedObjects( 52 + PhabricatorObjectQuery $query, 53 + array $names) { 54 + 55 + $id_map = array(); 56 + foreach ($names as $name) { 57 + $id = (int)substr($name, 1); 58 + $id_map[$id][] = $name; 59 + } 60 + 61 + $objects = id(new PassphraseCredentialQuery()) 62 + ->setViewer($query->getViewer()) 63 + ->withIDs(array_keys($id_map)) 64 + ->execute(); 65 + 66 + $results = array(); 67 + foreach ($objects as $id => $object) { 68 + foreach (idx($id_map, $id, array()) as $name) { 69 + $results[$name] = $object; 70 + } 71 + } 72 + 73 + return $results; 74 + } 75 + 76 + }
+137
src/applications/passphrase/query/PassphraseCredentialQuery.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $credentialTypes; 9 + private $providesTypes; 10 + private $isDestroyed; 11 + 12 + private $needSecrets; 13 + 14 + public function withIDs(array $ids) { 15 + $this->ids = $ids; 16 + return $this; 17 + } 18 + 19 + public function withPHIDs(array $phids) { 20 + $this->phids = $phids; 21 + return $this; 22 + } 23 + 24 + public function withCredentialTypes(array $credential_types) { 25 + $this->credentialTypes = $credential_types; 26 + return $this; 27 + } 28 + 29 + public function withProvidesTypes(array $provides_types) { 30 + $this->providesTypes = $provides_types; 31 + return $this; 32 + } 33 + 34 + public function withIsDestroyed($destroyed) { 35 + $this->isDestroyed = $destroyed; 36 + return $this; 37 + } 38 + 39 + public function needSecrets($need_secrets) { 40 + $this->needSecrets = $need_secrets; 41 + return $this; 42 + } 43 + 44 + protected function loadPage() { 45 + $table = new PassphraseCredential(); 46 + $conn_r = $table->establishConnection('r'); 47 + 48 + $rows = queryfx_all( 49 + $conn_r, 50 + 'SELECT * FROM %T %Q %Q %Q', 51 + $table->getTableName(), 52 + $this->buildWhereClause($conn_r), 53 + $this->buildOrderClause($conn_r), 54 + $this->buildLimitClause($conn_r)); 55 + 56 + return $table->loadAllFromArray($rows); 57 + } 58 + 59 + protected function willFilterPage(array $page) { 60 + if ($this->needSecrets) { 61 + $secret_ids = mpull($page, 'getSecretID'); 62 + $secret_ids = array_filter($secret_ids); 63 + 64 + $secrets = array(); 65 + if ($secret_ids) { 66 + $secret_objects = id(new PassphraseSecret())->loadAllWhere( 67 + 'id IN (%Ld)', 68 + $secret_ids); 69 + foreach ($secret_objects as $secret) { 70 + $secret_data = $secret->getSecretData(); 71 + $secrets[$secret->getID()] = new PhutilOpaqueEnvelope($secret_data); 72 + } 73 + } 74 + 75 + foreach ($page as $key => $credential) { 76 + $secret_id = $credential->getSecretID(); 77 + if (!$secret_id) { 78 + $credential->attachSecret(null); 79 + } else if (isset($secrets[$secret_id])) { 80 + $credential->attachSecret($secrets[$secret_id]); 81 + } else { 82 + unset($page[$key]); 83 + } 84 + } 85 + } 86 + 87 + return $page; 88 + } 89 + 90 + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 91 + $where = array(); 92 + 93 + $where[] = $this->buildPagingClause($conn_r); 94 + 95 + if ($this->ids) { 96 + $where[] = qsprintf( 97 + $conn_r, 98 + 'id IN (%Ld)', 99 + $this->ids); 100 + } 101 + 102 + if ($this->phids) { 103 + $where[] = qsprintf( 104 + $conn_r, 105 + 'phid IN (%Ls)', 106 + $this->phids); 107 + } 108 + 109 + if ($this->credentialTypes) { 110 + $where[] = qsprintf( 111 + $conn_r, 112 + 'credentialType in (%Ls)', 113 + $this->credentialTypes); 114 + } 115 + 116 + if ($this->providesTypes) { 117 + $where[] = qsprintf( 118 + $conn_r, 119 + 'providesType IN (%Ls)', 120 + $this->providesTypes); 121 + } 122 + 123 + if ($this->isDestroyed !== null) { 124 + $where[] = qsprintf( 125 + $conn_r, 126 + 'isDestroyed = %d', 127 + (int)$this->isDestroyed); 128 + } 129 + 130 + return $this->formatWhereClause($where); 131 + } 132 + 133 + public function getQueryApplicationClass() { 134 + return 'PhabricatorApplicationPassphrase'; 135 + } 136 + 137 + }
+73
src/applications/passphrase/query/PassphraseCredentialSearchEngine.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 7 + $saved = new PhabricatorSavedQuery(); 8 + 9 + $saved->setParameter( 10 + 'isDestroyed', 11 + $this->readBoolFromRequest($request, 'isDestroyed')); 12 + 13 + return $saved; 14 + } 15 + 16 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 17 + $query = id(new PassphraseCredentialQuery()); 18 + 19 + $destroyed = $saved->getParameter('isDestroyed'); 20 + if ($destroyed !== null) { 21 + $query->withIsDestroyed($destroyed); 22 + } 23 + 24 + return $query; 25 + } 26 + 27 + public function buildSearchForm( 28 + AphrontFormView $form, 29 + PhabricatorSavedQuery $saved_query) { 30 + 31 + $form->appendChild( 32 + id(new AphrontFormSelectControl()) 33 + ->setName('isDestroyed') 34 + ->setLabel(pht('Status')) 35 + ->setValue($this->getBoolFromQuery($saved_query, 'isDestroyed')) 36 + ->setOptions( 37 + array( 38 + '' => pht('Show All Credentials'), 39 + 'false' => pht('Show Only Active Credentials'), 40 + 'true' => pht('Show Only Destroyed Credentials'), 41 + ))); 42 + 43 + } 44 + 45 + protected function getURI($path) { 46 + return '/passphrase/'.$path; 47 + } 48 + 49 + public function getBuiltinQueryNames() { 50 + $names = array( 51 + 'active' => pht('Active Credentials'), 52 + 'all' => pht('All Credentials'), 53 + ); 54 + 55 + return $names; 56 + } 57 + 58 + public function buildSavedQueryFromBuiltin($query_key) { 59 + 60 + $query = $this->newSavedQuery(); 61 + $query->setQueryKey($query_key); 62 + 63 + switch ($query_key) { 64 + case 'all': 65 + return $query; 66 + case 'active': 67 + return $query->setParameter('isDestroyed', false); 68 + } 69 + 70 + return parent::buildSavedQueryFromBuiltin($query_key); 71 + } 72 + 73 + }
+10
src/applications/passphrase/query/PassphraseCredentialTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new PassphraseCredentialTransaction(); 8 + } 9 + 10 + }
+75
src/applications/passphrase/storage/PassphraseCredential.php
··· 1 + <?php 2 + 3 + final class PassphraseCredential extends PassphraseDAO 4 + implements PhabricatorPolicyInterface { 5 + 6 + protected $name; 7 + protected $credentialType; 8 + protected $providesType; 9 + protected $viewPolicy; 10 + protected $editPolicy; 11 + protected $description; 12 + protected $username; 13 + protected $secretID; 14 + protected $isDestroyed; 15 + 16 + private $secret = self::ATTACHABLE; 17 + 18 + public static function initializeNewCredential(PhabricatorUser $actor) { 19 + return id(new PassphraseCredential()) 20 + ->setName('') 21 + ->setUsername('') 22 + ->setIsDestroyed(0) 23 + ->setViewPolicy($actor->getPHID()) 24 + ->setEditPolicy($actor->getPHID()); 25 + } 26 + 27 + public function getConfiguration() { 28 + return array( 29 + self::CONFIG_AUX_PHID => true, 30 + ) + parent::getConfiguration(); 31 + } 32 + 33 + public function generatePHID() { 34 + return PhabricatorPHID::generateNewPHID( 35 + PassphrasePHIDTypeCredential::TYPECONST); 36 + } 37 + 38 + public function attachSecret(PhutilOpaqueEnvelope $secret = null) { 39 + $this->secret = $secret; 40 + return $this; 41 + } 42 + 43 + public function getSecret() { 44 + return $this->assertAttached($this->secret); 45 + } 46 + 47 + 48 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 49 + 50 + 51 + public function getCapabilities() { 52 + return array( 53 + PhabricatorPolicyCapability::CAN_VIEW, 54 + PhabricatorPolicyCapability::CAN_EDIT, 55 + ); 56 + } 57 + 58 + public function getPolicy($capability) { 59 + switch ($capability) { 60 + case PhabricatorPolicyCapability::CAN_VIEW: 61 + return $this->getViewPolicy(); 62 + case PhabricatorPolicyCapability::CAN_EDIT: 63 + return $this->getEditPolicy(); 64 + } 65 + } 66 + 67 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 68 + return false; 69 + } 70 + 71 + public function describeAutomaticCapability($capability) { 72 + return null; 73 + } 74 + 75 + }
+106
src/applications/passphrase/storage/PassphraseCredentialTransaction.php
··· 1 + <?php 2 + 3 + final class PassphraseCredentialTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_NAME = 'passphrase:name'; 7 + const TYPE_DESCRIPTION = 'passphrase:description'; 8 + const TYPE_USERNAME = 'passphrase:username'; 9 + const TYPE_SECRET_ID = 'passphrase:secretID'; 10 + const TYPE_DESTROY = 'passphrase:destroy'; 11 + 12 + public function getApplicationName() { 13 + return 'passphrase'; 14 + } 15 + 16 + public function getApplicationTransactionType() { 17 + return PassphrasePHIDTypeCredential::TYPECONST; 18 + } 19 + 20 + public function getApplicationTransactionCommentObject() { 21 + return null; 22 + } 23 + 24 + public function shouldHide() { 25 + $old = $this->getOldValue(); 26 + switch ($this->getTransactionType()) { 27 + case self::TYPE_DESCRIPTION: 28 + return ($old === null); 29 + case self::TYPE_USERNAME: 30 + return !strlen($old); 31 + } 32 + return parent::shouldHide(); 33 + } 34 + 35 + public function getTitle() { 36 + $old = $this->getOldValue(); 37 + $new = $this->getNewValue(); 38 + $author_phid = $this->getAuthorPHID(); 39 + 40 + switch ($this->getTransactionType()) { 41 + case self::TYPE_NAME: 42 + if ($old === null) { 43 + return pht( 44 + '%s created this credential.', 45 + $this->renderHandleLink($author_phid)); 46 + } else { 47 + return pht( 48 + '%s renamed this credential from "%s" to "%s".', 49 + $this->renderHandleLink($author_phid), 50 + $old, 51 + $new); 52 + } 53 + break; 54 + case self::TYPE_DESCRIPTION: 55 + return pht( 56 + '%s updated the description for this credential.', 57 + $this->renderHandleLink($author_phid)); 58 + case self::TYPE_USERNAME: 59 + if (strlen($old)) { 60 + return pht( 61 + '%s changed the username for this credential from "%s" to "%s".', 62 + $this->renderHandleLink($author_phid), 63 + $old, 64 + $new); 65 + } else { 66 + return pht( 67 + '%s set the username for this credential to "%s".', 68 + $this->renderHandleLink($author_phid), 69 + $new); 70 + } 71 + break; 72 + case self::TYPE_SECRET_ID: 73 + return pht( 74 + '%s updated the secret for this credential.', 75 + $this->renderHandleLink($author_phid)); 76 + case self::TYPE_DESTROY: 77 + return pht( 78 + '%s destroyed this credential.', 79 + $this->renderHandleLink($author_phid)); 80 + } 81 + 82 + return parent::getTitle(); 83 + } 84 + 85 + public function hasChangeDetails() { 86 + switch ($this->getTransactionType()) { 87 + case self::TYPE_DESCRIPTION: 88 + return true; 89 + } 90 + return parent::hasChangeDetails(); 91 + } 92 + 93 + public function renderChangeDetails(PhabricatorUser $viewer) { 94 + $old = $this->getOldValue(); 95 + $new = $this->getNewValue(); 96 + 97 + $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) 98 + ->setUser($viewer) 99 + ->setOldText(json_encode($old)) 100 + ->setNewText(json_encode($new)); 101 + 102 + return $view->render(); 103 + } 104 + 105 + 106 + }
+9
src/applications/passphrase/storage/PassphraseDAO.php
··· 1 + <?php 2 + 3 + abstract class PassphraseDAO extends PhabricatorLiskDAO { 4 + 5 + public function getApplicationName() { 6 + return 'passphrase'; 7 + } 8 + 9 + }
+13
src/applications/passphrase/storage/PassphraseSecret.php
··· 1 + <?php 2 + 3 + final class PassphraseSecret extends PassphraseDAO { 4 + 5 + protected $secretData; 6 + 7 + public function getConfiguration() { 8 + return array( 9 + self::CONFIG_TIMESTAMPS => false, 10 + ) + parent::getConfiguration(); 11 + } 12 + 13 + }
+6 -2
src/applications/policy/constants/PhabricatorPolicyType.php
··· 3 3 final class PhabricatorPolicyType extends PhabricatorPolicyConstants { 4 4 5 5 const TYPE_GLOBAL = 'global'; 6 + const TYPE_USER = 'user'; 6 7 const TYPE_CUSTOM = 'custom'; 7 8 const TYPE_PROJECT = 'project'; 8 9 const TYPE_MASKED = 'masked'; ··· 10 11 public static function getPolicyTypeOrder($type) { 11 12 static $map = array( 12 13 self::TYPE_GLOBAL => 0, 13 - self::TYPE_CUSTOM => 1, 14 - self::TYPE_PROJECT => 2, 14 + self::TYPE_USER => 1, 15 + self::TYPE_CUSTOM => 2, 16 + self::TYPE_PROJECT => 3, 15 17 self::TYPE_MASKED => 9, 16 18 ); 17 19 return idx($map, $type, 9); ··· 21 23 switch ($type) { 22 24 case self::TYPE_GLOBAL: 23 25 return pht('Basic Policies'); 26 + case self::TYPE_USER: 27 + return pht('User Policies'); 24 28 case self::TYPE_CUSTOM: 25 29 return pht('Advanced'); 26 30 case self::TYPE_PROJECT:
+6 -4
src/applications/policy/storage/PhabricatorPolicy.php
··· 66 66 $policy->setType(PhabricatorPolicyType::TYPE_PROJECT); 67 67 $policy->setName($handle->getName()); 68 68 break; 69 + case PhabricatorPeoplePHIDTypeUser::TYPECONST: 70 + $policy->setType(PhabricatorPolicyType::TYPE_USER); 71 + $policy->setName($handle->getFullName()); 72 + break; 69 73 case PhabricatorPolicyPHIDTypePolicy::TYPECONST: 70 74 // TODO: This creates a weird handle-based version of a rule policy. 71 75 // It behaves correctly, but can't be applied since it doesn't have ··· 138 142 PhabricatorPolicies::POLICY_NOONE => 'policy-noone', 139 143 ); 140 144 return idx($map, $this->getPHID(), 'policy-unknown'); 141 - break; 145 + case PhabricatorPolicyType::TYPE_USER: 146 + return 'policy-user'; 142 147 case PhabricatorPolicyType::TYPE_PROJECT: 143 148 return 'policy-project'; 144 - break; 145 149 case PhabricatorPolicyType::TYPE_CUSTOM: 146 150 case PhabricatorPolicyType::TYPE_MASKED: 147 151 return 'policy-custom'; 148 - break; 149 152 default: 150 153 return 'policy-unknown'; 151 - break; 152 154 } 153 155 } 154 156
+33
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1129 1129 } 1130 1130 1131 1131 1132 + /** 1133 + * Check for a missing text field. 1134 + * 1135 + * A text field is missing if the object has no value and there are no 1136 + * transactions which set a value, or if the transactions remove the value. 1137 + * This method is intended to make implementing @{method:validateTransaction} 1138 + * more convenient: 1139 + * 1140 + * $missing = $this->validateIsEmptyTextField( 1141 + * $object->getName(), 1142 + * $xactions); 1143 + * 1144 + * This will return `true` if the net effect of the object and transactions 1145 + * is an empty field. 1146 + * 1147 + * @param wild Current field value. 1148 + * @param list<PhabricatorApplicationTransaction> Transactions editing the 1149 + * field. 1150 + * @return bool True if the field will be an empty text field after edits. 1151 + */ 1152 + protected function validateIsEmptyTextField($field_value, array $xactions) { 1153 + if (strlen($field_value) && empty($xactions)) { 1154 + return false; 1155 + } 1156 + 1157 + if ($xactions && strlen(last($xactions)->getNewValue())) { 1158 + return false; 1159 + } 1160 + 1161 + return true; 1162 + } 1163 + 1164 + 1132 1165 /* -( Implicit CCs )------------------------------------------------------- */ 1133 1166 1134 1167
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 212 212 'type' => 'db', 213 213 'name' => 'nuance', 214 214 ), 215 + 'db.passphrase' => array( 216 + 'type' => 'db', 217 + 'name' => 'passphrase', 218 + ), 215 219 '0000.legacy.sql' => array( 216 220 'type' => 'sql', 217 221 'name' => $this->getPatchPath('0000.legacy.sql'), ··· 1759 1763 '20131118.ownerorder.php' => array( 1760 1764 'type' => 'php', 1761 1765 'name' => $this->getPatchPath('20131118.ownerorder.php'), 1766 + ), 1767 + '20131119.passphrase.sql' => array( 1768 + 'type' => 'sql', 1769 + 'name' => $this->getPatchPath('20131119.passphrase.sql'), 1762 1770 ), 1763 1771 ); 1764 1772 }
+1
src/view/form/control/AphrontFormPolicyControl.php
··· 103 103 $options, 104 104 array( 105 105 PhabricatorPolicyType::TYPE_GLOBAL, 106 + PhabricatorPolicyType::TYPE_USER, 106 107 PhabricatorPolicyType::TYPE_CUSTOM, 107 108 PhabricatorPolicyType::TYPE_PROJECT, 108 109 ));