Path: blob/master/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php
13442 views
<?php12final class AlmanacManagementRegisterWorkflow3extends AlmanacManagementWorkflow {45protected function didConstruct() {6$this7->setName('register')8->setSynopsis(pht('Register this host as an Almanac device.'))9->setArguments(10array(11array(12'name' => 'device',13'param' => 'name',14'help' => pht('Almanac device name to register.'),15),16array(17'name' => 'private-key',18'param' => 'key',19'help' => pht('Path to a private key for the host.'),20),21array(22'name' => 'identify-as',23'param' => 'name',24'help' => pht(25'Specify an alternate host identity. This is an advanced '.26'feature which allows a pool of devices to share credentials.'),27),28array(29'name' => 'force',30'help' => pht(31'Register this host even if keys already exist on disk.'),32),33));34}3536public function execute(PhutilArgumentParser $args) {37$viewer = $this->getViewer();3839$device_name = $args->getArg('device');40if (!strlen($device_name)) {41throw new PhutilArgumentUsageException(42pht('Specify a device with --device.'));43}4445$device = id(new AlmanacDeviceQuery())46->setViewer($viewer)47->withNames(array($device_name))48->executeOne();49if (!$device) {50throw new PhutilArgumentUsageException(51pht('No such device "%s" exists!', $device_name));52}5354$identify_as = $args->getArg('identify-as');5556$raw_device = $device_name;57if (strlen($identify_as)) {58$raw_device = $identify_as;59}6061$identity_device = id(new AlmanacDeviceQuery())62->setViewer($viewer)63->withNames(array($raw_device))64->executeOne();65if (!$identity_device) {66throw new PhutilArgumentUsageException(67pht(68'No such device "%s" exists!', $raw_device));69}7071$private_key_path = $args->getArg('private-key');72if (!strlen($private_key_path)) {73throw new PhutilArgumentUsageException(74pht('Specify a private key with --private-key.'));75}7677if (!Filesystem::pathExists($private_key_path)) {78throw new PhutilArgumentUsageException(79pht('No private key exists at path "%s"!', $private_key_path));80}8182$raw_private_key = Filesystem::readFile($private_key_path);8384$phd_user = PhabricatorEnv::getEnvConfig('phd.user');85if (!$phd_user) {86throw new PhutilArgumentUsageException(87pht(88'Config option "phd.user" is not set. You must set this option '.89'so the private key can be stored with the correct permissions.'));90}9192$tmp = new TempFile();93list($err) = exec_manual('chown %s %s', $phd_user, $tmp);94if ($err) {95throw new PhutilArgumentUsageException(96pht(97'Unable to change ownership of an identity file to daemon user '.98'"%s". Run this command as %s or root.',99$phd_user,100$phd_user));101}102103$stored_public_path = AlmanacKeys::getKeyPath('device.pub');104$stored_private_path = AlmanacKeys::getKeyPath('device.key');105$stored_device_path = AlmanacKeys::getKeyPath('device.id');106107if (!$args->getArg('force')) {108if (Filesystem::pathExists($stored_public_path)) {109throw new PhutilArgumentUsageException(110pht(111'This host already has a registered public key ("%s"). '.112'Remove this key before registering the host, or use '.113'--force to overwrite it.',114Filesystem::readablePath($stored_public_path)));115}116117if (Filesystem::pathExists($stored_private_path)) {118throw new PhutilArgumentUsageException(119pht(120'This host already has a registered private key ("%s"). '.121'Remove this key before registering the host, or use '.122'--force to overwrite it.',123Filesystem::readablePath($stored_private_path)));124}125}126127// NOTE: We're writing the private key here so we can change permissions128// on it without causing weird side effects to the file specified with129// the `--private-key` flag. The file needs to have restrictive permissions130// before `ssh-keygen` will willingly operate on it.131$tmp_private = new TempFile();132Filesystem::changePermissions($tmp_private, 0600);133execx('chown %s %s', $phd_user, $tmp_private);134Filesystem::writeFile($tmp_private, $raw_private_key);135136list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private);137138$key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key);139140$public_key = id(new PhabricatorAuthSSHKeyQuery())141->setViewer($this->getViewer())142->withKeys(array($key_object))143->withIsActive(true)144->executeOne();145146if (!$public_key) {147throw new PhutilArgumentUsageException(148pht(149'The public key corresponding to the given private key is unknown. '.150'Associate the public key with an Almanac device in the web '.151'interface before registering hosts with it.'));152}153154if ($public_key->getObjectPHID() !== $device->getPHID()) {155$public_phid = $public_key->getObjectPHID();156$public_handles = $viewer->loadHandles(array($public_phid));157$public_handle = $public_handles[$public_phid];158159throw new PhutilArgumentUsageException(160pht(161'The public key corresponding to the given private key is already '.162'associated with an object ("%s") other than the specified '.163'device ("%s"). You can not use a single private key to identify '.164'multiple devices or users.',165$public_handle->getFullName(),166$device->getName()));167}168169if (!$public_key->getIsTrusted()) {170throw new PhutilArgumentUsageException(171pht(172'The public key corresponding to the given private key is '.173'properly associated with the device, but is not yet trusted. '.174'Trust this key before registering devices with it.'));175}176177echo tsprintf(178"%s\n",179pht('Installing public key...'));180181$tmp_public = new TempFile();182Filesystem::changePermissions($tmp_public, 0600);183execx('chown %s %s', $phd_user, $tmp_public);184Filesystem::writeFile($tmp_public, $raw_public_key);185execx('mv -f %s %s', $tmp_public, $stored_public_path);186187echo tsprintf(188"%s\n",189pht('Installing private key...'));190execx('mv -f %s %s', $tmp_private, $stored_private_path);191192echo tsprintf(193"%s\n",194pht('Installing device %s...', $raw_device));195196// The permissions on this file are more open because the webserver also197// needs to read it.198$tmp_device = new TempFile();199Filesystem::changePermissions($tmp_device, 0644);200execx('chown %s %s', $phd_user, $tmp_device);201Filesystem::writeFile($tmp_device, $raw_device);202execx('mv -f %s %s', $tmp_device, $stored_device_path);203204echo tsprintf(205"**<bg:green> %s </bg>** %s\n",206pht('HOST REGISTERED'),207pht(208'This host has been registered as "%s" and a trusted keypair '.209'has been installed.',210$raw_device));211}212213}214215216