diff --git a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php index 1c40af5795..384395c182 100644 --- a/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php +++ b/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php @@ -1,256 +1,282 @@ isAdministrativeEdit = $is_administrative_edit; + return $this; + } + + public function getIsAdministrativeEdit() { + return $this->isAdministrativeEdit; + } + public function getEditorApplicationClass() { return 'PhabricatorAuthApplication'; } public function getEditorObjectsDescription() { return pht('SSH Keys'); } public function getTransactionTypes() { $types = parent::getTransactionTypes(); $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_NAME; $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_KEY; $types[] = PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE; return $types; } protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: return $object->getName(); case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: return $object->getEntireKey(); case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: return !$object->getIsActive(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: return $xaction->getNewValue(); case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: return (bool)$xaction->getNewValue(); } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $value = $xaction->getNewValue(); switch ($xaction->getTransactionType()) { case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: $object->setName($value); return; case PhabricatorAuthSSHKeyTransaction::TYPE_KEY: $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($value); $type = $public_key->getType(); $body = $public_key->getBody(); $comment = $public_key->getComment(); $object->setKeyType($type); $object->setKeyBody($body); $object->setKeyComment($comment); return; case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: if ($value) { $new = null; } else { $new = 1; } $object->setIsActive($new); return; } } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { return; } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorAuthSSHKeyTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('SSH key name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorAuthSSHKeyTransaction::TYPE_KEY; $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('SSH key material is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); try { $public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), $ex->getMessage(), $xaction); } } } break; case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE: foreach ($xactions as $xaction) { if (!$xaction->getNewValue()) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht('SSH keys can not be reactivated.'), $xaction); } } break; } return $errors; } protected function didCatchDuplicateKeyException( PhabricatorLiskDAO $object, array $xactions, Exception $ex) { $errors = array(); $errors[] = new PhabricatorApplicationTransactionValidationError( PhabricatorAuthSSHKeyTransaction::TYPE_KEY, pht('Duplicate'), pht( 'This public key is already associated with another user or device. '. 'Each key must unambiguously identify a single unique owner.'), null); throw new PhabricatorApplicationTransactionValidationException($errors); } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function getMailSubjectPrefix() { return pht('[SSH Key]'); } protected function getMailThreadID(PhabricatorLiskDAO $object) { return 'ssh-key-'.$object->getPHID(); } protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { // After making any change to an SSH key, drop the authfile cache so it // is regenerated the next time anyone authenticates. PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache(); return $xactions; } protected function getMailTo(PhabricatorLiskDAO $object) { return $object->getObject()->getSSHKeyNotifyPHIDs(); } protected function getMailCC(PhabricatorLiskDAO $object) { return array(); } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PhabricatorAuthSSHKeyReplyHandler()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $name = $object->getName(); $phid = $object->getPHID(); $mail = id(new PhabricatorMetaMTAMail()) ->setSubject(pht('SSH Key %d: %s', $id, $name)) ->addHeader('Thread-Topic', $phid); // The primary value of this mail is alerting users to account compromises, // so force delivery. In particular, this mail should still be delivered // even if "self mail" is disabled. $mail->setForceDelivery(true); return $mail; } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); - $body->addTextSection( - pht('SECURITY WARNING'), - pht( - 'If you do not recognize this change, it may indicate your account '. - 'has been compromised.')); + if (!$this->getIsAdministrativeEdit()) { + $body->addTextSection( + pht('SECURITY WARNING'), + pht( + 'If you do not recognize this change, it may indicate your account '. + 'has been compromised.')); + } $detail_uri = $object->getURI(); $detail_uri = PhabricatorEnv::getProductionURI($detail_uri); $body->addLinkSection(pht('SSH KEY DETAIL'), $detail_uri); return $body; } + + protected function getCustomWorkerState() { + return array( + 'isAdministrativeEdit' => $this->isAdministrativeEdit, + ); + } + + protected function loadCustomWorkerState(array $state) { + $this->isAdministrativeEdit = idx($state, 'isAdministrativeEdit'); + return $this; + } + + } diff --git a/src/applications/auth/revoker/PhabricatorAuthSSHRevoker.php b/src/applications/auth/revoker/PhabricatorAuthSSHRevoker.php index aafb60cfca..a18a2e7812 100644 --- a/src/applications/auth/revoker/PhabricatorAuthSSHRevoker.php +++ b/src/applications/auth/revoker/PhabricatorAuthSSHRevoker.php @@ -1,52 +1,53 @@ revokeWithQuery($query); } public function revokeCredentialsFrom($object) { $query = id(new PhabricatorAuthSSHKeyQuery()) ->withObjectPHIDs(array($object->getPHID())); return $this->revokeWithQuery($query); } private function revokeWithQuery(PhabricatorAuthSSHKeyQuery $query) { $viewer = $this->getViewer(); // We're only going to revoke keys which have not already been revoked. $ssh_keys = $query ->setViewer($viewer) ->withIsActive(true) ->execute(); $content_source = PhabricatorContentSource::newForSource( PhabricatorDaemonContentSource::SOURCECONST); $auth_phid = id(new PhabricatorAuthApplication())->getPHID(); foreach ($ssh_keys as $ssh_key) { $xactions = array(); $xactions[] = $ssh_key->getApplicationTransactionTemplate() ->setTransactionType(PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE) ->setNewValue(1); $editor = $ssh_key->getApplicationTransactionEditor() ->setActor($viewer) ->setActingAsPHID($auth_phid) ->setContinueOnNoEffect(true) ->setContinueOnMissingFields(true) ->setContentSource($content_source) + ->setIsAdministrativeEdit(true) ->applyTransactions($ssh_key, $xactions); } return count($ssh_keys); } }