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