public function getPublicKey(PhabricatorUser $viewer, PassphraseCredential $credential)
 {
     $key = PassphraseSSHKey::loadFromPHID($credential->getPHID(), $viewer);
     $file = $key->getKeyfileEnvelope();
     list($stdout) = execx('ssh-keygen -y -f %P', $file);
     return $stdout;
 }
 public function didInitializeNewCredential(PhabricatorUser $actor, PassphraseCredential $credential)
 {
     $pair = PhabricatorSSHKeyGenerator::generateKeypair();
     list($public_key, $private_key) = $pair;
     $credential->attachSecret(new PhutilOpaqueEnvelope($private_key));
     return $credential;
 }
 protected function loadPage()
 {
     $table = new PassphraseCredential();
     $conn_r = $table->establishConnection('r');
     $rows = queryfx_all($conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r));
     return $table->loadAllFromArray($rows);
 }
 private function buildActionView(PassphraseCredential $credential, PassphraseCredentialType $type)
 {
     $viewer = $this->getRequest()->getUser();
     $id = $credential->getID();
     $is_locked = $credential->getIsLocked();
     if ($is_locked) {
         $credential_lock_text = pht('Locked Permanently');
         $credential_lock_icon = 'fa-lock';
     } else {
         $credential_lock_text = pht('Lock Permanently');
         $credential_lock_icon = 'fa-unlock';
     }
     $allow_conduit = $credential->getAllowConduit();
     if ($allow_conduit) {
         $credential_conduit_text = pht('Prevent Conduit Access');
         $credential_conduit_icon = 'fa-ban';
     } else {
         $credential_conduit_text = pht('Allow Conduit Access');
         $credential_conduit_icon = 'fa-wrench';
     }
     $actions = id(new PhabricatorActionListView())->setObjectURI('/K' . $id)->setObject($credential)->setUser($viewer);
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $credential, PhabricatorPolicyCapability::CAN_EDIT);
     $actions->addAction(id(new PhabricatorActionView())->setName(pht('Edit Credential'))->setIcon('fa-pencil')->setHref($this->getApplicationURI("edit/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     if (!$credential->getIsDestroyed()) {
         $actions->addAction(id(new PhabricatorActionView())->setName(pht('Destroy Credential'))->setIcon('fa-times')->setHref($this->getApplicationURI("destroy/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(true));
         $actions->addAction(id(new PhabricatorActionView())->setName(pht('Show Secret'))->setIcon('fa-eye')->setHref($this->getApplicationURI("reveal/{$id}/"))->setDisabled(!$can_edit || $is_locked)->setWorkflow(true));
         if ($type->hasPublicKey()) {
             $actions->addAction(id(new PhabricatorActionView())->setName(pht('Show Public Key'))->setIcon('fa-download')->setHref($this->getApplicationURI("public/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(true));
         }
         $actions->addAction(id(new PhabricatorActionView())->setName($credential_conduit_text)->setIcon($credential_conduit_icon)->setHref($this->getApplicationURI("conduit/{$id}/"))->setDisabled(!$can_edit)->setWorkflow(true));
         $actions->addAction(id(new PhabricatorActionView())->setName($credential_lock_text)->setIcon($credential_lock_icon)->setHref($this->getApplicationURI("lock/{$id}/"))->setDisabled(!$can_edit || $is_locked)->setWorkflow(true));
     }
     return $actions;
 }
 private function validateCredential(PassphraseCredential $credential, $provides_type)
 {
     $type = $credential->getImplementation();
     if (!$type) {
         throw new Exception(pht('Credential "%s" is of unknown type "%s"!', $credential->getMonogram(), $credential->getCredentialType()));
     }
     if ($type->getProvidesType() !== $provides_type) {
         throw new Exception(pht('Credential "%s" must provide "%s", but provides "%s"!', $credential->getMonogram(), $provides_type, $type->getProvidesType()));
     }
 }
 public function testURIGeneration()
 {
     $svn = PhabricatorRepositoryType::REPOSITORY_TYPE_SVN;
     $git = PhabricatorRepositoryType::REPOSITORY_TYPE_GIT;
     $hg = PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL;
     $user = $this->generateNewTestUser();
     $http_secret = id(new PassphraseSecret())->setSecretData('quack')->save();
     $http_credential = PassphraseCredential::initializeNewCredential($user)->setCredentialType(PassphrasePasswordCredentialType::CREDENTIAL_TYPE)->setProvidesType(PassphrasePasswordCredentialType::PROVIDES_TYPE)->setUsername('duck')->setSecretID($http_secret->getID())->save();
     $repo = PhabricatorRepository::initializeNewRepository($user)->setVersionControlSystem($svn)->setName(pht('Test Repo'))->setCallsign('TESTREPO')->setCredentialPHID($http_credential->getPHID())->save();
     // Test HTTP URIs.
     $repo->setDetail('remote-uri', 'http://example.com/');
     $repo->setVersionControlSystem($svn);
     $this->assertEqual('http://example.com/', $repo->getRemoteURI());
     $this->assertEqual('http://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('http://example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     $repo->setVersionControlSystem($git);
     $this->assertEqual('http://example.com/', $repo->getRemoteURI());
     $this->assertEqual('http://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('http://*****:*****@example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     $repo->setVersionControlSystem($hg);
     $this->assertEqual('http://example.com/', $repo->getRemoteURI());
     $this->assertEqual('http://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('http://*****:*****@example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     // Test SSH URIs.
     $repo->setDetail('remote-uri', 'ssh://example.com/');
     $repo->setVersionControlSystem($svn);
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
     $this->assertEqual('ssh://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     $repo->setVersionControlSystem($git);
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
     $this->assertEqual('ssh://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     $repo->setVersionControlSystem($hg);
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURI());
     $this->assertEqual('ssh://example.com/', $repo->getPublicCloneURI());
     $this->assertEqual('ssh://example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
     // Test Git URIs.
     $repo->setDetail('remote-uri', 'git@example.com:path.git');
     $repo->setVersionControlSystem($git);
     $this->assertEqual('git@example.com:path.git', $repo->getRemoteURI());
     $this->assertEqual('git@example.com:path.git', $repo->getPublicCloneURI());
     $this->assertEqual('git@example.com:path.git', $repo->getRemoteURIEnvelope()->openEnvelope());
     // Test SVN "Import Only" paths.
     $repo->setDetail('remote-uri', 'http://example.com/');
     $repo->setVersionControlSystem($svn);
     $repo->setDetail('svn-subpath', 'projects/example/');
     $this->assertEqual('http://example.com/', $repo->getRemoteURI());
     $this->assertEqual('http://example.com/projects/example/', $repo->getPublicCloneURI());
     $this->assertEqual('http://example.com/', $repo->getRemoteURIEnvelope()->openEnvelope());
 }
    echo pht('...will migrate.') . "\n";
}
$passphrase = new PassphraseSecret();
$passphrase->openTransaction();
$table->openTransaction();
foreach ($map as $credential_type => $credential_usernames) {
    $type = PassphraseCredentialType::getTypeByConstant($credential_type);
    foreach ($credential_usernames as $username => $credential_secrets) {
        foreach ($credential_secrets as $secret_plaintext => $repositories) {
            $callsigns = mpull($repositories, 'getCallsign');
            $signs = implode(', ', $callsigns);
            $name = pht('Migrated Repository Credential (%s)', id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(128)->truncateString($signs));
            echo pht('Creating: %s...', $name) . "\n";
            $secret = id(new PassphraseSecret())->setSecretData($secret_plaintext)->save();
            $secret_id = $secret->getID();
            $credential = PassphraseCredential::initializeNewCredential($viewer)->setCredentialType($type->getCredentialType())->setProvidesType($type->getProvidesType())->setViewPolicy(PhabricatorPolicies::POLICY_ADMIN)->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)->setName($name)->setUsername($username)->setSecretID($secret_id);
            $credential->setPHID($credential->generatePHID());
            queryfx($credential->establishConnection('w'), 'INSERT INTO %T (name, credentialType, providesType, viewPolicy,
          editPolicy, description, username, secretID, isDestroyed,
          phid, dateCreated, dateModified)
          VALUES (%s, %s, %s, %s, %s, %s, %s, %d, %d, %s, %d, %d)', $credential->getTableName(), $credential->getName(), $credential->getCredentialType(), $credential->getProvidesType(), $credential->getViewPolicy(), $credential->getEditPolicy(), $credential->getDescription(), $credential->getUsername(), $credential->getSecretID(), $credential->getIsDestroyed(), $credential->getPHID(), time(), time());
            foreach ($repositories as $repository) {
                queryfx($conn_w, 'UPDATE %T SET credentialPHID = %s WHERE id = %d', $table->getTableName(), $credential->getPHID(), $repository->getID());
                $edge_type = PhabricatorObjectUsesCredentialsEdgeType::EDGECONST;
                id(new PhabricatorEdgeEditor())->addEdge($repository->getPHID(), $edge_type, $credential->getPHID())->save();
            }
        }
    }
}
$table->saveTransaction();
$passphrase->saveTransaction();
 public function handleRequest(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $id = $request->getURIData('id');
     if ($id) {
         $credential = id(new PassphraseCredentialQuery())->setViewer($viewer)->withIDs(array($id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne();
         if (!$credential) {
             return new Aphront404Response();
         }
         $type = $this->getCredentialType($credential->getCredentialType());
         $is_new = false;
     } else {
         $type_const = $request->getStr('type');
         $type = $this->getCredentialType($type_const);
         if (!$type->isCreateable()) {
             throw new Exception(pht('Credential has noncreateable type "%s"!', $type_const));
         }
         $credential = PassphraseCredential::initializeNewCredential($viewer)->setCredentialType($type->getCredentialType())->setProvidesType($type->getProvidesType());
         $is_new = true;
         // Prefill username if provided.
         $credential->setUsername((string) $request->getStr('username'));
         if (!$request->getStr('isInitialized')) {
             $type->didInitializeNewCredential($viewer, $credential);
         }
     }
     $errors = array();
     $v_name = $credential->getName();
     $e_name = true;
     $v_desc = $credential->getDescription();
     $v_space = $credential->getSpacePHID();
     $v_username = $credential->getUsername();
     $e_username = true;
     $v_is_locked = false;
     $bullet = "•";
     $v_secret = $credential->getSecretID() ? str_repeat($bullet, 32) : null;
     if ($is_new && $v_secret === null) {
         // If we're creating a new credential, the credential type may have
         // populated the secret for us (for example, generated an SSH key). In
         // this case,
         try {
             $v_secret = $credential->getSecret()->openEnvelope();
         } catch (Exception $ex) {
             // Ignore this.
         }
     }
     $validation_exception = null;
     $errors = array();
     $e_password = null;
     if ($request->isFormPost()) {
         $v_name = $request->getStr('name');
         $v_desc = $request->getStr('description');
         $v_username = $request->getStr('username');
         $v_view_policy = $request->getStr('viewPolicy');
         $v_edit_policy = $request->getStr('editPolicy');
         $v_is_locked = $request->getStr('lock');
         $v_secret = $request->getStr('secret');
         $v_space = $request->getStr('spacePHID');
         $v_password = $request->getStr('password');
         $v_decrypt = $v_secret;
         $env_secret = new PhutilOpaqueEnvelope($v_secret);
         $env_password = new PhutilOpaqueEnvelope($v_password);
         if ($type->requiresPassword($env_secret)) {
             if (strlen($v_password)) {
                 $v_decrypt = $type->decryptSecret($env_secret, $env_password);
                 if ($v_decrypt === null) {
                     $e_password = pht('Incorrect');
                     $errors[] = pht('This key requires a password, but the password you provided ' . 'is incorrect.');
                 } else {
                     $v_decrypt = $v_decrypt->openEnvelope();
                 }
             } else {
                 $e_password = pht('Required');
                 $errors[] = pht('This key requires a password. You must provide the password ' . 'for the key.');
             }
         }
         if (!$errors) {
             $type_name = PassphraseCredentialTransaction::TYPE_NAME;
             $type_desc = PassphraseCredentialTransaction::TYPE_DESCRIPTION;
             $type_username = PassphraseCredentialTransaction::TYPE_USERNAME;
             $type_destroy = PassphraseCredentialTransaction::TYPE_DESTROY;
             $type_secret_id = PassphraseCredentialTransaction::TYPE_SECRET_ID;
             $type_is_locked = PassphraseCredentialTransaction::TYPE_LOCK;
             $type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;
             $type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY;
             $type_space = PhabricatorTransactions::TYPE_SPACE;
             $xactions = array();
             $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_name)->setNewValue($v_name);
             $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_desc)->setNewValue($v_desc);
             $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_view_policy)->setNewValue($v_view_policy);
             $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_edit_policy)->setNewValue($v_edit_policy);
             $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_space)->setNewValue($v_space);
             // Open a transaction in case we're writing a new secret; this limits
             // the amount of code which handles secret plaintexts.
             $credential->openTransaction();
             if (!$credential->getIsLocked()) {
                 if ($type->shouldRequireUsername()) {
                     $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_username)->setNewValue($v_username);
                 }
                 // If some value other than a sequence of bullets was provided for
                 // the credential, update it. In particular, note that we are
                 // explicitly allowing empty secrets: one use case is HTTP auth where
                 // the username is a secret token which covers both identity and
                 // authentication.
                 if (!preg_match('/^(' . $bullet . ')+$/', trim($v_decrypt))) {
                     // If the credential was previously destroyed, restore it when it is
                     // edited if a secret is provided.
                     $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_destroy)->setNewValue(0);
                     $new_secret = id(new PassphraseSecret())->setSecretData($v_decrypt)->save();
                     $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_secret_id)->setNewValue($new_secret->getID());
                 }
                 $xactions[] = id(new PassphraseCredentialTransaction())->setTransactionType($type_is_locked)->setNewValue($v_is_locked);
             }
             try {
                 $editor = id(new PassphraseCredentialTransactionEditor())->setActor($viewer)->setContinueOnNoEffect(true)->setContentSourceFromRequest($request)->applyTransactions($credential, $xactions);
                 $credential->saveTransaction();
                 if ($request->isAjax()) {
                     return id(new AphrontAjaxResponse())->setContent(array('phid' => $credential->getPHID(), 'name' => 'K' . $credential->getID() . ' ' . $credential->getName()));
                 } else {
                     return id(new AphrontRedirectResponse())->setURI('/K' . $credential->getID());
                 }
             } catch (PhabricatorApplicationTransactionValidationException $ex) {
                 $credential->killTransaction();
                 $validation_exception = $ex;
                 $e_name = $ex->getShortMessage($type_name);
                 $e_username = $ex->getShortMessage($type_username);
                 $credential->setViewPolicy($v_view_policy);
                 $credential->setEditPolicy($v_edit_policy);
             }
         }
     }
     $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($credential)->execute();
     $secret_control = $type->newSecretControl();
     $credential_is_locked = $credential->getIsLocked();
     $form = id(new AphrontFormView())->setUser($viewer)->addHiddenInput('isInitialized', true)->appendChild(id(new AphrontFormTextControl())->setName('name')->setLabel(pht('Name'))->setValue($v_name)->setError($e_name))->appendChild(id(new AphrontFormTextAreaControl())->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)->setName('description')->setLabel(pht('Description'))->setValue($v_desc))->appendChild(id(new AphrontFormMarkupControl())->setLabel(pht('Credential Type'))->setValue($type->getCredentialTypeName()))->appendChild(id(new AphrontFormDividerControl()))->appendControl(id(new AphrontFormPolicyControl())->setName('viewPolicy')->setPolicyObject($credential)->setCapability(PhabricatorPolicyCapability::CAN_VIEW)->setPolicies($policies))->appendControl(id(new AphrontFormPolicyControl())->setName('editPolicy')->setPolicyObject($credential)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setPolicies($policies))->appendChild(id(new AphrontFormDividerControl()));
     if ($credential_is_locked) {
         $form->appendRemarkupInstructions(pht('This credential is permanently locked and can not be edited.'));
     }
     if ($type->shouldRequireUsername()) {
         $form->appendChild(id(new AphrontFormTextControl())->setName('username')->setLabel(pht('Login/Username'))->setValue($v_username)->setDisabled($credential_is_locked)->setError($e_username));
     }
     $form->appendChild($secret_control->setName('secret')->setLabel($type->getSecretLabel())->setDisabled($credential_is_locked)->setValue($v_secret));
     if ($type->shouldShowPasswordField()) {
         $form->appendChild(id(new AphrontFormPasswordControl())->setDisableAutocomplete(true)->setName('password')->setLabel($type->getPasswordLabel())->setDisabled($credential_is_locked)->setError($e_password));
     }
     if ($is_new) {
         $form->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('lock', 1, array(phutil_tag('strong', array(), pht('Lock Permanently:')), ' ', pht('Prevent the secret from being revealed or changed.')), $v_is_locked)->setDisabled($credential_is_locked));
     }
     $crumbs = $this->buildApplicationCrumbs();
     if ($is_new) {
         $title = pht('Create Credential');
         $header = pht('Create New Credential');
         $crumbs->addTextCrumb(pht('Create'));
         $cancel_uri = $this->getApplicationURI();
     } else {
         $title = pht('Edit Credential');
         $header = pht('Edit Credential %s', 'K' . $credential->getID());
         $crumbs->addTextCrumb('K' . $credential->getID(), '/K' . $credential->getID());
         $crumbs->addTextCrumb(pht('Edit'));
         $cancel_uri = '/K' . $credential->getID();
     }
     if ($request->isAjax()) {
         if ($errors) {
             $errors = id(new PHUIInfoView())->setErrors($errors);
         }
         $dialog = id(new AphrontDialogView())->setUser($viewer)->setWidth(AphrontDialogView::WIDTH_FORM)->setTitle($title)->appendChild($errors)->appendChild($form->buildLayoutView())->addSubmitButton(pht('Create Credential'))->addCancelButton($cancel_uri);
         return id(new AphrontDialogResponse())->setDialog($dialog);
     }
     $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save'))->addCancelButton($cancel_uri));
     $box = id(new PHUIObjectBoxView())->setHeaderText($header)->setFormErrors($errors)->setValidationException($validation_exception)->setForm($form);
     return $this->buildApplicationPage(array($crumbs, $box), array('title' => $title));
 }