Path: blob/master/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php
12256 views
<?php12final class PhabricatorAuthSSHKeyEditor3extends PhabricatorApplicationTransactionEditor {45private $isAdministrativeEdit;67public function setIsAdministrativeEdit($is_administrative_edit) {8$this->isAdministrativeEdit = $is_administrative_edit;9return $this;10}1112public function getIsAdministrativeEdit() {13return $this->isAdministrativeEdit;14}1516public function getEditorApplicationClass() {17return 'PhabricatorAuthApplication';18}1920public function getEditorObjectsDescription() {21return pht('SSH Keys');22}2324public function getTransactionTypes() {25$types = parent::getTransactionTypes();2627$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_NAME;28$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_KEY;29$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE;3031return $types;32}3334protected function getCustomTransactionOldValue(35PhabricatorLiskDAO $object,36PhabricatorApplicationTransaction $xaction) {3738switch ($xaction->getTransactionType()) {39case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:40return $object->getName();41case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:42return $object->getEntireKey();43case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:44return !$object->getIsActive();45}4647}4849protected function getCustomTransactionNewValue(50PhabricatorLiskDAO $object,51PhabricatorApplicationTransaction $xaction) {5253switch ($xaction->getTransactionType()) {54case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:55case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:56return $xaction->getNewValue();57case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:58return (bool)$xaction->getNewValue();59}60}6162protected function applyCustomInternalTransaction(63PhabricatorLiskDAO $object,64PhabricatorApplicationTransaction $xaction) {6566$value = $xaction->getNewValue();67switch ($xaction->getTransactionType()) {68case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:69$object->setName($value);70return;71case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:72$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($value);7374$type = $public_key->getType();75$body = $public_key->getBody();76$comment = $public_key->getComment();7778$object->setKeyType($type);79$object->setKeyBody($body);80$object->setKeyComment($comment);81return;82case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:83if ($value) {84$new = null;85} else {86$new = 1;87}8889$object->setIsActive($new);90return;91}92}9394protected function applyCustomExternalTransaction(95PhabricatorLiskDAO $object,96PhabricatorApplicationTransaction $xaction) {97return;98}99100protected function validateTransaction(101PhabricatorLiskDAO $object,102$type,103array $xactions) {104105$errors = parent::validateTransaction($object, $type, $xactions);106$viewer = $this->requireActor();107108switch ($type) {109case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:110$missing = $this->validateIsEmptyTextField(111$object->getName(),112$xactions);113114if ($missing) {115$error = new PhabricatorApplicationTransactionValidationError(116$type,117pht('Required'),118pht('SSH key name is required.'),119nonempty(last($xactions), null));120121$error->setIsMissingFieldError(true);122$errors[] = $error;123}124break;125126case PhabricatorAuthSSHKeyTransaction::TYPE_KEY;127$missing = $this->validateIsEmptyTextField(128$object->getName(),129$xactions);130131if ($missing) {132$error = new PhabricatorApplicationTransactionValidationError(133$type,134pht('Required'),135pht('SSH key material is required.'),136nonempty(last($xactions), null));137138$error->setIsMissingFieldError(true);139$errors[] = $error;140} else {141foreach ($xactions as $xaction) {142$new = $xaction->getNewValue();143144try {145$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new);146} catch (Exception $ex) {147$errors[] = new PhabricatorApplicationTransactionValidationError(148$type,149pht('Invalid'),150$ex->getMessage(),151$xaction);152continue;153}154155// The database does not have a unique key on just the <keyBody>156// column because we allow multiple accounts to revoke the same157// key, so we can't rely on database constraints to prevent users158// from adding keys that are on the revocation list back to their159// accounts. Explicitly check for a revoked copy of the key.160161$revoked_keys = id(new PhabricatorAuthSSHKeyQuery())162->setViewer($viewer)163->withObjectPHIDs(array($object->getObjectPHID()))164->withIsActive(0)165->withKeys(array($public_key))166->execute();167if ($revoked_keys) {168$errors[] = new PhabricatorApplicationTransactionValidationError(169$type,170pht('Revoked'),171pht(172'This key has been revoked. Choose or generate a new, '.173'unique key.'),174$xaction);175continue;176}177}178}179break;180181case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:182foreach ($xactions as $xaction) {183if (!$xaction->getNewValue()) {184$errors[] = new PhabricatorApplicationTransactionValidationError(185$type,186pht('Invalid'),187pht('SSH keys can not be reactivated.'),188$xaction);189}190}191break;192}193194return $errors;195}196197protected function didCatchDuplicateKeyException(198PhabricatorLiskDAO $object,199array $xactions,200Exception $ex) {201202$errors = array();203$errors[] = new PhabricatorApplicationTransactionValidationError(204PhabricatorAuthSSHKeyTransaction::TYPE_KEY,205pht('Duplicate'),206pht(207'This public key is already associated with another user or device. '.208'Each key must unambiguously identify a single unique owner.'),209null);210211throw new PhabricatorApplicationTransactionValidationException($errors);212}213214215protected function shouldSendMail(216PhabricatorLiskDAO $object,217array $xactions) {218return true;219}220221protected function getMailSubjectPrefix() {222return pht('[SSH Key]');223}224225protected function getMailThreadID(PhabricatorLiskDAO $object) {226return 'ssh-key-'.$object->getPHID();227}228229protected function applyFinalEffects(230PhabricatorLiskDAO $object,231array $xactions) {232233// After making any change to an SSH key, drop the authfile cache so it234// is regenerated the next time anyone authenticates.235PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();236237return $xactions;238}239240241protected function getMailTo(PhabricatorLiskDAO $object) {242return $object->getObject()->getSSHKeyNotifyPHIDs();243}244245protected function getMailCC(PhabricatorLiskDAO $object) {246return array();247}248249protected function buildReplyHandler(PhabricatorLiskDAO $object) {250return id(new PhabricatorAuthSSHKeyReplyHandler())251->setMailReceiver($object);252}253254protected function buildMailTemplate(PhabricatorLiskDAO $object) {255$id = $object->getID();256$name = $object->getName();257258$mail = id(new PhabricatorMetaMTAMail())259->setSubject(pht('SSH Key %d: %s', $id, $name));260261// The primary value of this mail is alerting users to account compromises,262// so force delivery. In particular, this mail should still be delivered263// even if "self mail" is disabled.264$mail->setForceDelivery(true);265266return $mail;267}268269protected function buildMailBody(270PhabricatorLiskDAO $object,271array $xactions) {272273$body = parent::buildMailBody($object, $xactions);274275if (!$this->getIsAdministrativeEdit()) {276$body->addTextSection(277pht('SECURITY WARNING'),278pht(279'If you do not recognize this change, it may indicate your account '.280'has been compromised.'));281}282283$detail_uri = $object->getURI();284$detail_uri = PhabricatorEnv::getProductionURI($detail_uri);285286$body->addLinkSection(pht('SSH KEY DETAIL'), $detail_uri);287288return $body;289}290291292protected function getCustomWorkerState() {293return array(294'isAdministrativeEdit' => $this->isAdministrativeEdit,295);296}297298protected function loadCustomWorkerState(array $state) {299$this->isAdministrativeEdit = idx($state, 'isAdministrativeEdit');300return $this;301}302303304}305306307