public function getKeyfileEnvelope()
 {
     $credential = $this->requireCredential();
     $file_type = PassphraseCredentialTypeSSHPrivateKeyFile::CREDENTIAL_TYPE;
     if ($credential->getCredentialType() != $file_type) {
         // If the credential does not store a file, write the key text out to a
         // temporary file so we can pass it to `ssh`.
         if (!$this->keyFile) {
             $secret = $credential->getSecret();
             if (!$secret) {
                 throw new Exception(pht('Attempting to use a credential ("%s") but the credential ' . 'secret has been destroyed!', $credential->getMonogram()));
             }
             $temporary_file = new TempFile('passphrase-ssh-key');
             Filesystem::changePermissions($temporary_file, 0600);
             Filesystem::writeFile($temporary_file, $secret->openEnvelope());
             $this->keyFile = $temporary_file;
         }
         return new PhutilOpaqueEnvelope((string) $this->keyFile);
     }
     return $credential->getSecret();
 }
 private function installHook($path)
 {
     $this->log('%s', pht('Installing commit hook to "%s"...', $path));
     $repository = $this->getRepository();
     $identifier = $this->getHookContextIdentifier($repository);
     $root = dirname(phutil_get_library_root('phabricator'));
     $bin = $root . '/bin/commit-hook';
     $full_php_path = Filesystem::resolveBinary('php');
     $cmd = csprintf('exec %s -f %s -- %s "$@"', $full_php_path, $bin, $identifier);
     $hook = "#!/bin/sh\nexport TERM=dumb\n{$cmd}\n";
     Filesystem::writeFile($path, $hook);
     Filesystem::changePermissions($path, 0755);
 }
 /**
  * Create a directory in a manner similar to mkdir(), but throw detailed
  * exceptions on failure.
  *
  * @param  string    Path to directory. The parent directory must exist and
  *                   be writable.
  * @param  int       Permission umask. Note that umask is in octal, so you
  *                   should specify it as, e.g., `0777', not `777'. By
  *                   default, these permissions are very liberal (0777).
  * @param  boolean   Recursivly create directories.  Default to false
  * @return string    Path to the created directory.
  *
  * @task   directory
  */
 public static function createDirectory($path, $umask = 0777, $recursive = false)
 {
     $path = self::resolvePath($path);
     if (is_dir($path)) {
         Filesystem::changePermissions($path, $umask);
         return $path;
     }
     $dir = dirname($path);
     if ($recursive && !file_exists($dir)) {
         // Note: We could do this with the recursive third parameter of mkdir(),
         // but then we loose the helpful FilesystemExceptions we normally get.
         self::createDirectory($dir, $umask, true);
     }
     self::assertIsDirectory($dir);
     self::assertExists($dir);
     self::assertWritable($dir);
     self::assertNotExists($path);
     if (!mkdir($path, $umask)) {
         throw new FilesystemException($path, "Failed to create directory `{$path}'.");
     }
     // Need to change premissions explicitly because mkdir does something
     // slightly different. mkdir(2) man page:
     // 'The parameter mode specifies the permissions to use. It is modified by
     // the process's umask in the usual way: the permissions of the created
     // directory are (mode & ~umask & 0777)."'
     Filesystem::changePermissions($path, $umask);
     return $path;
 }
 private function lintFile($file, ArcanistLinter $linter)
 {
     $linter = clone $linter;
     $contents = Filesystem::readFile($file);
     $contents = preg_split('/^~{4,}\\n/m', $contents);
     if (count($contents) < 2) {
         throw new Exception(pht("Expected '%s' separating test case and results.", '~~~~~~~~~~'));
     }
     list($data, $expect, $xform, $config) = array_merge($contents, array(null, null));
     $basename = basename($file);
     if ($config) {
         $config = phutil_json_decode($config);
     } else {
         $config = array();
     }
     PhutilTypeSpec::checkMap($config, array('config' => 'optional map<string, wild>', 'path' => 'optional string', 'mode' => 'optional string', 'stopped' => 'optional bool'));
     $exception = null;
     $after_lint = null;
     $messages = null;
     $exception_message = false;
     $caught_exception = false;
     try {
         $tmp = new TempFile($basename);
         Filesystem::writeFile($tmp, $data);
         $full_path = (string) $tmp;
         $mode = idx($config, 'mode');
         if ($mode) {
             Filesystem::changePermissions($tmp, octdec($mode));
         }
         $dir = dirname($full_path);
         $path = basename($full_path);
         $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile($dir, null, pht('Unit Test'));
         $configuration_manager = new ArcanistConfigurationManager();
         $configuration_manager->setWorkingCopyIdentity($working_copy);
         $engine = new ArcanistUnitTestableLintEngine();
         $engine->setWorkingCopy($working_copy);
         $engine->setConfigurationManager($configuration_manager);
         $path_name = idx($config, 'path', $path);
         $engine->setPaths(array($path_name));
         $linter->addPath($path_name);
         $linter->addData($path_name, $data);
         foreach (idx($config, 'config', array()) as $key => $value) {
             $linter->setLinterConfigurationValue($key, $value);
         }
         $engine->addLinter($linter);
         $engine->addFileData($path_name, $data);
         $results = $engine->run();
         $this->assertEqual(1, count($results), pht('Expect one result returned by linter.'));
         $assert_stopped = idx($config, 'stopped');
         if ($assert_stopped !== null) {
             $this->assertEqual($assert_stopped, $linter->didStopAllLinters(), $assert_stopped ? pht('Expect linter to be stopped.') : pht('Expect linter to not be stopped.'));
         }
         $result = reset($results);
         $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result);
         $after_lint = $patcher->getModifiedFileContent();
     } catch (PhutilTestTerminatedException $ex) {
         throw $ex;
     } catch (Exception $exception) {
         $caught_exception = true;
         if ($exception instanceof PhutilAggregateException) {
             $caught_exception = false;
             foreach ($exception->getExceptions() as $ex) {
                 if ($ex instanceof ArcanistUsageException || $ex instanceof ArcanistMissingLinterException) {
                     $this->assertSkipped($ex->getMessage());
                 } else {
                     $caught_exception = true;
                 }
             }
         } else {
             if ($exception instanceof ArcanistUsageException || $exception instanceof ArcanistMissingLinterException) {
                 $this->assertSkipped($exception->getMessage());
             }
         }
         $exception_message = $exception->getMessage() . "\n\n" . $exception->getTraceAsString();
     }
     $this->assertEqual(false, $caught_exception, $exception_message);
     $this->compareLint($basename, $expect, $result);
     $this->compareTransform($xform, $after_lint);
 }
 public function execute(PhutilArgumentParser $args)
 {
     $viewer = $this->getViewer();
     $device_name = $args->getArg('device');
     if (!strlen($device_name)) {
         throw new PhutilArgumentUsageException(pht('Specify a device with --device.'));
     }
     $device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($device_name))->executeOne();
     if (!$device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $device_name));
     }
     $identify_as = $args->getArg('identify-as');
     $raw_device = $device_name;
     if (strlen($identify_as)) {
         $raw_device = $identify_as;
     }
     $identity_device = id(new AlmanacDeviceQuery())->setViewer($viewer)->withNames(array($raw_device))->executeOne();
     if (!$identity_device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $raw_device));
     }
     $private_key_path = $args->getArg('private-key');
     if (!strlen($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Specify a private key with --private-key.'));
     }
     if (!Filesystem::pathExists($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('No private key exists at path "%s"!', $private_key_path));
     }
     $raw_private_key = Filesystem::readFile($private_key_path);
     $phd_user = PhabricatorEnv::getEnvConfig('phd.user');
     if (!$phd_user) {
         throw new PhutilArgumentUsageException(pht('Config option "phd.user" is not set. You must set this option ' . 'so the private key can be stored with the correct permissions.'));
     }
     $tmp = new TempFile();
     list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
     if ($err) {
         throw new PhutilArgumentUsageException(pht('Unable to change ownership of an identity file to daemon user ' . '"%s". Run this command as %s or root.', $phd_user, $phd_user));
     }
     $stored_public_path = AlmanacKeys::getKeyPath('device.pub');
     $stored_private_path = AlmanacKeys::getKeyPath('device.key');
     $stored_device_path = AlmanacKeys::getKeyPath('device.id');
     if (!$args->getArg('force')) {
         if (Filesystem::pathExists($stored_public_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered public key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_public_path)));
         }
         if (Filesystem::pathExists($stored_private_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered private key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_private_path)));
         }
     }
     // NOTE: We're writing the private key here so we can change permissions
     // on it without causing weird side effects to the file specified with
     // the `--private-key` flag. The file needs to have restrictive permissions
     // before `ssh-keygen` will willingly operate on it.
     $tmp_private = new TempFile();
     Filesystem::changePermissions($tmp_private, 0600);
     execx('chown %s %s', $phd_user, $tmp_private);
     Filesystem::writeFile($tmp_private, $raw_private_key);
     list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);
     $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
     $public_key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withKeys(array($key_object))->withIsActive(true)->executeOne();
     if (!$public_key) {
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is not ' . 'yet known to Phabricator. Associate the public key with an ' . 'Almanac device in the web interface before registering hosts ' . 'with it.'));
     }
     if ($public_key->getObjectPHID() !== $device->getPHID()) {
         $public_phid = $public_key->getObjectPHID();
         $public_handles = $viewer->loadHandles(array($public_phid));
         $public_handle = $public_handles[$public_phid];
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is already ' . 'associated with an object ("%s") other than the specified ' . 'device ("%s"). You can not use a single private key to identify ' . 'multiple devices or users.', $public_handle->getFullName(), $device->getName()));
     }
     if (!$public_key->getIsTrusted()) {
         throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'properly associated with the device, but is not yet trusted. ' . 'Trust this key before registering devices with it.'));
     }
     echo tsprintf("%s\n", pht('Installing public key...'));
     $tmp_public = new TempFile();
     Filesystem::changePermissions($tmp_public, 0600);
     execx('chown %s %s', $phd_user, $tmp_public);
     Filesystem::writeFile($tmp_public, $raw_public_key);
     execx('mv -f %s %s', $tmp_public, $stored_public_path);
     echo tsprintf("%s\n", pht('Installing private key...'));
     execx('mv -f %s %s', $tmp_private, $stored_private_path);
     echo tsprintf("%s\n", pht('Installing device %s...', $raw_device));
     // The permissions on this file are more open because the webserver also
     // needs to read it.
     $tmp_device = new TempFile();
     Filesystem::changePermissions($tmp_device, 0644);
     execx('chown %s %s', $phd_user, $tmp_device);
     Filesystem::writeFile($tmp_device, $raw_device);
     execx('mv -f %s %s', $tmp_device, $stored_device_path);
     echo tsprintf("**<bg:green> %s </bg>** %s\n", pht('HOST REGISTERED'), pht('This host has been registered as "%s" and a trusted keypair ' . 'has been installed.', $raw_device));
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $device_name = $args->getArg('device');
     if (!strlen($device_name)) {
         throw new PhutilArgumentUsageException(pht('Specify a device with --device.'));
     }
     $device = id(new AlmanacDeviceQuery())->setViewer($this->getViewer())->withNames(array($device_name))->executeOne();
     if (!$device) {
         throw new PhutilArgumentUsageException(pht('No such device "%s" exists!', $device_name));
     }
     $private_key_path = $args->getArg('private-key');
     if (!strlen($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Specify a private key with --private-key.'));
     }
     if (!Filesystem::pathExists($private_key_path)) {
         throw new PhutilArgumentUsageException(pht('Private key "%s" does not exist!', $private_key_path));
     }
     $raw_private_key = Filesystem::readFile($private_key_path);
     $phd_user = PhabricatorEnv::getEnvConfig('phd.user');
     if (!$phd_user) {
         throw new PhutilArgumentUsageException(pht('Config option "phd.user" is not set. You must set this option ' . 'so the private key can be stored with the correct permissions.'));
     }
     $tmp = new TempFile();
     list($err) = exec_manual('chown %s %s', $phd_user, $tmp);
     if ($err) {
         throw new PhutilArgumentUsageException(pht('Unable to change ownership of a file to daemon user "%s". Run ' . 'this command as %s or root.', $phd_user, $phd_user));
     }
     $stored_public_path = AlmanacKeys::getKeyPath('device.pub');
     $stored_private_path = AlmanacKeys::getKeyPath('device.key');
     $stored_device_path = AlmanacKeys::getKeyPath('device.id');
     if (!$args->getArg('force')) {
         if (Filesystem::pathExists($stored_public_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered public key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_public_path)));
         }
         if (Filesystem::pathExists($stored_private_path)) {
             throw new PhutilArgumentUsageException(pht('This host already has a registered private key ("%s"). ' . 'Remove this key before registering the host, or use ' . '--force to overwrite it.', Filesystem::readablePath($stored_private_path)));
         }
     }
     // NOTE: We're writing the private key here so we can change permissions
     // on it without causing weird side effects to the file specified with
     // the `--private-key` flag. The file needs to have restrictive permissions
     // before `ssh-keygen` will willingly operate on it.
     $tmp_private = new TempFile();
     Filesystem::changePermissions($tmp_private, 0600);
     execx('chown %s %s', $phd_user, $tmp_private);
     Filesystem::writeFile($tmp_private, $raw_private_key);
     list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);
     $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);
     $public_key = id(new PhabricatorAuthSSHKeyQuery())->setViewer($this->getViewer())->withKeys(array($key_object))->executeOne();
     if ($public_key) {
         if ($public_key->getObjectPHID() !== $device->getPHID()) {
             throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with an object other than the specified ' . 'device. You can not use a single private key to identify ' . 'multiple devices or users.'));
         } else {
             if (!$public_key->getIsTrusted()) {
                 throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with the device, but is not trusted. ' . 'Registering this key would trust the other entities which ' . 'hold it. Use a unique key, or explicitly enable trust for the ' . 'current key.'));
             } else {
                 if (!$args->getArg('allow-key-reuse')) {
                     throw new PhutilArgumentUsageException(pht('The public key corresponding to the given private key is ' . 'already associated with the device. If you do not want to ' . 'use a unique key, use --allow-key-reuse to permit ' . 'reassociation.'));
                 }
             }
         }
     } else {
         $public_key = id(new PhabricatorAuthSSHKey())->setObjectPHID($device->getPHID())->attachObject($device)->setName($device->getSSHKeyDefaultName())->setKeyType($key_object->getType())->setKeyBody($key_object->getBody())->setKeyComment(pht('Registered'))->setIsTrusted(1);
     }
     $console->writeOut("%s\n", pht('Installing public key...'));
     $tmp_public = new TempFile();
     Filesystem::changePermissions($tmp_public, 0600);
     execx('chown %s %s', $phd_user, $tmp_public);
     Filesystem::writeFile($tmp_public, $raw_public_key);
     execx('mv -f %s %s', $tmp_public, $stored_public_path);
     $console->writeOut("%s\n", pht('Installing private key...'));
     execx('mv -f %s %s', $tmp_private, $stored_private_path);
     $raw_device = $device_name;
     $identify_as = $args->getArg('identify-as');
     if (strlen($identify_as)) {
         $raw_device = $identify_as;
     }
     $console->writeOut("%s\n", pht('Installing device %s...', $raw_device));
     // The permissions on this file are more open because the webserver also
     // needs to read it.
     $tmp_device = new TempFile();
     Filesystem::changePermissions($tmp_device, 0644);
     execx('chown %s %s', $phd_user, $tmp_device);
     Filesystem::writeFile($tmp_device, $raw_device);
     execx('mv -f %s %s', $tmp_device, $stored_device_path);
     if (!$public_key->getID()) {
         $console->writeOut("%s\n", pht('Registering device key...'));
         $public_key->save();
     }
     $console->writeOut("**<bg:green> %s </bg>** %s\n", pht('HOST REGISTERED'), pht('This host has been registered as "%s" and a trusted keypair ' . 'has been installed.', $raw_device));
 }