Path: blob/master/src/applications/passphrase/controller/PassphraseCredentialEditController.php
12256 views
<?php12final class PassphraseCredentialEditController extends PassphraseController {34public function handleRequest(AphrontRequest $request) {5$viewer = $request->getViewer();6$id = $request->getURIData('id');78if ($id) {9$credential = id(new PassphraseCredentialQuery())10->setViewer($viewer)11->withIDs(array($id))12->requireCapabilities(13array(14PhabricatorPolicyCapability::CAN_VIEW,15PhabricatorPolicyCapability::CAN_EDIT,16))17->executeOne();18if (!$credential) {19return new Aphront404Response();20}2122$type = $this->getCredentialType($credential->getCredentialType());23$type_const = $type->getCredentialType();2425$is_new = false;26} else {27$type_const = $request->getStr('type');28$type = $this->getCredentialType($type_const);2930if (!$type->isCreateable()) {31throw new Exception(32pht(33'Credential has noncreateable type "%s"!',34$type_const));35}3637$credential = PassphraseCredential::initializeNewCredential($viewer)38->setCredentialType($type->getCredentialType())39->setProvidesType($type->getProvidesType())40->attachImplementation($type);4142$is_new = true;4344// Prefill username if provided.45$credential->setUsername((string)$request->getStr('username'));4647if (!$request->getStr('isInitialized')) {48$type->didInitializeNewCredential($viewer, $credential);49}50}5152$errors = array();5354$v_name = $credential->getName();55$e_name = true;5657$v_desc = $credential->getDescription();58$v_space = $credential->getSpacePHID();5960$v_username = $credential->getUsername();61$e_username = true;6263$v_is_locked = false;6465$bullet = "\xE2\x80\xA2";6667$v_secret = $credential->getSecretID() ? str_repeat($bullet, 32) : null;68if ($is_new && ($v_secret === null)) {69// If we're creating a new credential, the credential type may have70// populated the secret for us (for example, generated an SSH key). In71// this case,72try {73$v_secret = $credential->getSecret()->openEnvelope();74} catch (Exception $ex) {75// Ignore this.76}77}7879$validation_exception = null;80$errors = array();81$e_password = null;82$e_secret = null;83if ($request->isFormPost()) {8485$v_name = $request->getStr('name');86$v_desc = $request->getStr('description');87$v_username = $request->getStr('username');88$v_view_policy = $request->getStr('viewPolicy');89$v_edit_policy = $request->getStr('editPolicy');90$v_is_locked = $request->getStr('lock');9192$v_secret = $request->getStr('secret');93$v_space = $request->getStr('spacePHID');94$v_password = $request->getStr('password');95$v_decrypt = $v_secret;9697$env_secret = new PhutilOpaqueEnvelope($v_secret);98$env_password = new PhutilOpaqueEnvelope($v_password);99100$has_secret = !preg_match('/^('.$bullet.')+$/', trim($v_decrypt));101102// Validate and repair SSH private keys, and apply passwords if they103// are provided. See T13454 for discussion.104105// This should eventually be refactored to be modular rather than a106// hard-coded set of behaviors here in the Controller, but this is107// likely a fairly extensive change.108109$is_ssh = ($type instanceof PassphraseSSHPrivateKeyTextCredentialType);110111if ($is_ssh && $has_secret) {112$old_object = PhabricatorAuthSSHPrivateKey::newFromRawKey($env_secret);113114if (strlen($v_password)) {115$old_object->setPassphrase($env_password);116}117118try {119$new_object = $old_object->newBarePrivateKey();120$v_decrypt = $new_object->getKeyBody()->openEnvelope();121} catch (PhabricatorAuthSSHPrivateKeyException $ex) {122$errors[] = $ex->getMessage();123124if ($ex->isFormatException()) {125$e_secret = pht('Invalid');126}127if ($ex->isPassphraseException()) {128$e_password = pht('Invalid');129}130}131}132133if (!$errors) {134$type_name =135PassphraseCredentialNameTransaction::TRANSACTIONTYPE;136$type_desc =137PassphraseCredentialDescriptionTransaction::TRANSACTIONTYPE;138$type_username =139PassphraseCredentialUsernameTransaction::TRANSACTIONTYPE;140$type_destroy =141PassphraseCredentialDestroyTransaction::TRANSACTIONTYPE;142$type_secret_id =143PassphraseCredentialSecretIDTransaction::TRANSACTIONTYPE;144$type_is_locked =145PassphraseCredentialLockTransaction::TRANSACTIONTYPE;146147$type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;148$type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY;149$type_space = PhabricatorTransactions::TYPE_SPACE;150151$xactions = array();152153$xactions[] = id(new PassphraseCredentialTransaction())154->setTransactionType($type_name)155->setNewValue($v_name);156157$xactions[] = id(new PassphraseCredentialTransaction())158->setTransactionType($type_desc)159->setNewValue($v_desc);160161$xactions[] = id(new PassphraseCredentialTransaction())162->setTransactionType($type_view_policy)163->setNewValue($v_view_policy);164165$xactions[] = id(new PassphraseCredentialTransaction())166->setTransactionType($type_edit_policy)167->setNewValue($v_edit_policy);168169$xactions[] = id(new PassphraseCredentialTransaction())170->setTransactionType($type_space)171->setNewValue($v_space);172173// Open a transaction in case we're writing a new secret; this limits174// the amount of code which handles secret plaintexts.175$credential->openTransaction();176177if (!$credential->getIsLocked()) {178if ($type->shouldRequireUsername()) {179$xactions[] = id(new PassphraseCredentialTransaction())180->setTransactionType($type_username)181->setNewValue($v_username);182}183184// If some value other than a sequence of bullets was provided for185// the credential, update it. In particular, note that we are186// explicitly allowing empty secrets: one use case is HTTP auth where187// the username is a secret token which covers both identity and188// authentication.189190if ($has_secret) {191// If the credential was previously destroyed, restore it when it is192// edited if a secret is provided.193$xactions[] = id(new PassphraseCredentialTransaction())194->setTransactionType($type_destroy)195->setNewValue(0);196197$new_secret = id(new PassphraseSecret())198->setSecretData($v_decrypt)199->save();200201$xactions[] = id(new PassphraseCredentialTransaction())202->setTransactionType($type_secret_id)203->setNewValue($new_secret->getID());204}205206$xactions[] = id(new PassphraseCredentialTransaction())207->setTransactionType($type_is_locked)208->setNewValue($v_is_locked);209}210211try {212$editor = id(new PassphraseCredentialTransactionEditor())213->setActor($viewer)214->setContinueOnNoEffect(true)215->setContentSourceFromRequest($request)216->applyTransactions($credential, $xactions);217218$credential->saveTransaction();219220if ($request->isAjax()) {221return id(new AphrontAjaxResponse())->setContent(222array(223'phid' => $credential->getPHID(),224'name' => 'K'.$credential->getID().' '.$credential->getName(),225));226} else {227return id(new AphrontRedirectResponse())228->setURI('/K'.$credential->getID());229}230} catch (PhabricatorApplicationTransactionValidationException $ex) {231$credential->killTransaction();232233$validation_exception = $ex;234235$e_name = $ex->getShortMessage($type_name);236$e_username = $ex->getShortMessage($type_username);237238$credential->setViewPolicy($v_view_policy);239$credential->setEditPolicy($v_edit_policy);240}241}242}243244$policies = id(new PhabricatorPolicyQuery())245->setViewer($viewer)246->setObject($credential)247->execute();248249$secret_control = $type->newSecretControl();250$credential_is_locked = $credential->getIsLocked();251252$form = id(new AphrontFormView())253->setUser($viewer)254->addHiddenInput('isInitialized', true)255->addHiddenInput('type', $type_const)256->appendChild(257id(new AphrontFormTextControl())258->setName('name')259->setLabel(pht('Name'))260->setValue($v_name)261->setError($e_name))262->appendChild(263id(new PhabricatorRemarkupControl())264->setUser($viewer)265->setName('description')266->setLabel(pht('Description'))267->setValue($v_desc))268->appendChild(269id(new AphrontFormDividerControl()))270->appendControl(271id(new AphrontFormPolicyControl())272->setName('viewPolicy')273->setPolicyObject($credential)274->setSpacePHID($v_space)275->setCapability(PhabricatorPolicyCapability::CAN_VIEW)276->setPolicies($policies))277->appendControl(278id(new AphrontFormPolicyControl())279->setName('editPolicy')280->setPolicyObject($credential)281->setCapability(PhabricatorPolicyCapability::CAN_EDIT)282->setPolicies($policies))283->appendChild(284id(new AphrontFormDividerControl()));285286if ($credential_is_locked) {287$form->appendRemarkupInstructions(288pht('This credential is permanently locked and can not be edited.'));289}290291if ($type->shouldRequireUsername()) {292$form->appendChild(293id(new AphrontFormTextControl())294->setName('username')295->setLabel(pht('Login/Username'))296->setValue($v_username)297->setDisabled($credential_is_locked)298->setError($e_username));299}300301$form->appendChild(302$secret_control303->setName('secret')304->setLabel($type->getSecretLabel())305->setDisabled($credential_is_locked)306->setValue($v_secret)307->setError($e_secret));308309if ($type->shouldShowPasswordField()) {310$form->appendChild(311id(new AphrontFormPasswordControl())312->setDisableAutocomplete(true)313->setName('password')314->setLabel($type->getPasswordLabel())315->setDisabled($credential_is_locked)316->setError($e_password));317}318319if ($is_new) {320$form->appendChild(321id(new AphrontFormCheckboxControl())322->addCheckbox(323'lock',3241,325array(326phutil_tag('strong', array(), pht('Lock Permanently:')),327' ',328pht('Prevent the secret from being revealed or changed.'),329),330$v_is_locked)331->setDisabled($credential_is_locked));332}333334$crumbs = $this->buildApplicationCrumbs();335$crumbs->setBorder(true);336337if ($is_new) {338$title = pht('New Credential: %s', $type->getCredentialTypeName());339$crumbs->addTextCrumb(pht('Create'));340$cancel_uri = $this->getApplicationURI();341} else {342$title = pht('Edit Credential: %s', $credential->getName());343$crumbs->addTextCrumb(344'K'.$credential->getID(),345'/K'.$credential->getID());346$crumbs->addTextCrumb(pht('Edit'));347$cancel_uri = '/K'.$credential->getID();348}349350if ($request->isAjax()) {351if ($errors) {352$errors = id(new PHUIInfoView())->setErrors($errors);353}354355return $this->newDialog()356->setWidth(AphrontDialogView::WIDTH_FORM)357->setTitle($title)358->appendChild($errors)359->appendChild($form->buildLayoutView())360->addSubmitButton(pht('Create Credential'))361->addCancelButton($cancel_uri);362}363364$form->appendChild(365id(new AphrontFormSubmitControl())366->setValue(pht('Save'))367->addCancelButton($cancel_uri));368369$box = id(new PHUIObjectBoxView())370->setHeaderText($title)371->setFormErrors($errors)372->setValidationException($validation_exception)373->setBackground(PHUIObjectBoxView::WHITE_CONFIG)374->setForm($form);375376$view = id(new PHUITwoColumnView())377->setFooter(array(378$box,379));380381return $this->newPage()382->setTitle($title)383->setCrumbs($crumbs)384->appendChild($view);385}386387private function getCredentialType($type_const) {388$type = PassphraseCredentialType::getTypeByConstant($type_const);389390if (!$type) {391throw new Exception(392pht('Credential has invalid type "%s"!', $type_const));393}394395return $type;396}397398}399400401