Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/auth/editor/PhabricatorAuthSSHKeyEditor.php
12256 views
1
<?php
2
3
final class PhabricatorAuthSSHKeyEditor
4
extends PhabricatorApplicationTransactionEditor {
5
6
private $isAdministrativeEdit;
7
8
public function setIsAdministrativeEdit($is_administrative_edit) {
9
$this->isAdministrativeEdit = $is_administrative_edit;
10
return $this;
11
}
12
13
public function getIsAdministrativeEdit() {
14
return $this->isAdministrativeEdit;
15
}
16
17
public function getEditorApplicationClass() {
18
return 'PhabricatorAuthApplication';
19
}
20
21
public function getEditorObjectsDescription() {
22
return pht('SSH Keys');
23
}
24
25
public function getTransactionTypes() {
26
$types = parent::getTransactionTypes();
27
28
$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_NAME;
29
$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_KEY;
30
$types[] = PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE;
31
32
return $types;
33
}
34
35
protected function getCustomTransactionOldValue(
36
PhabricatorLiskDAO $object,
37
PhabricatorApplicationTransaction $xaction) {
38
39
switch ($xaction->getTransactionType()) {
40
case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
41
return $object->getName();
42
case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:
43
return $object->getEntireKey();
44
case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:
45
return !$object->getIsActive();
46
}
47
48
}
49
50
protected function getCustomTransactionNewValue(
51
PhabricatorLiskDAO $object,
52
PhabricatorApplicationTransaction $xaction) {
53
54
switch ($xaction->getTransactionType()) {
55
case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
56
case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:
57
return $xaction->getNewValue();
58
case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:
59
return (bool)$xaction->getNewValue();
60
}
61
}
62
63
protected function applyCustomInternalTransaction(
64
PhabricatorLiskDAO $object,
65
PhabricatorApplicationTransaction $xaction) {
66
67
$value = $xaction->getNewValue();
68
switch ($xaction->getTransactionType()) {
69
case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
70
$object->setName($value);
71
return;
72
case PhabricatorAuthSSHKeyTransaction::TYPE_KEY:
73
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($value);
74
75
$type = $public_key->getType();
76
$body = $public_key->getBody();
77
$comment = $public_key->getComment();
78
79
$object->setKeyType($type);
80
$object->setKeyBody($body);
81
$object->setKeyComment($comment);
82
return;
83
case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:
84
if ($value) {
85
$new = null;
86
} else {
87
$new = 1;
88
}
89
90
$object->setIsActive($new);
91
return;
92
}
93
}
94
95
protected function applyCustomExternalTransaction(
96
PhabricatorLiskDAO $object,
97
PhabricatorApplicationTransaction $xaction) {
98
return;
99
}
100
101
protected function validateTransaction(
102
PhabricatorLiskDAO $object,
103
$type,
104
array $xactions) {
105
106
$errors = parent::validateTransaction($object, $type, $xactions);
107
$viewer = $this->requireActor();
108
109
switch ($type) {
110
case PhabricatorAuthSSHKeyTransaction::TYPE_NAME:
111
$missing = $this->validateIsEmptyTextField(
112
$object->getName(),
113
$xactions);
114
115
if ($missing) {
116
$error = new PhabricatorApplicationTransactionValidationError(
117
$type,
118
pht('Required'),
119
pht('SSH key name is required.'),
120
nonempty(last($xactions), null));
121
122
$error->setIsMissingFieldError(true);
123
$errors[] = $error;
124
}
125
break;
126
127
case PhabricatorAuthSSHKeyTransaction::TYPE_KEY;
128
$missing = $this->validateIsEmptyTextField(
129
$object->getName(),
130
$xactions);
131
132
if ($missing) {
133
$error = new PhabricatorApplicationTransactionValidationError(
134
$type,
135
pht('Required'),
136
pht('SSH key material is required.'),
137
nonempty(last($xactions), null));
138
139
$error->setIsMissingFieldError(true);
140
$errors[] = $error;
141
} else {
142
foreach ($xactions as $xaction) {
143
$new = $xaction->getNewValue();
144
145
try {
146
$public_key = PhabricatorAuthSSHPublicKey::newFromRawKey($new);
147
} catch (Exception $ex) {
148
$errors[] = new PhabricatorApplicationTransactionValidationError(
149
$type,
150
pht('Invalid'),
151
$ex->getMessage(),
152
$xaction);
153
continue;
154
}
155
156
// The database does not have a unique key on just the <keyBody>
157
// column because we allow multiple accounts to revoke the same
158
// key, so we can't rely on database constraints to prevent users
159
// from adding keys that are on the revocation list back to their
160
// accounts. Explicitly check for a revoked copy of the key.
161
162
$revoked_keys = id(new PhabricatorAuthSSHKeyQuery())
163
->setViewer($viewer)
164
->withObjectPHIDs(array($object->getObjectPHID()))
165
->withIsActive(0)
166
->withKeys(array($public_key))
167
->execute();
168
if ($revoked_keys) {
169
$errors[] = new PhabricatorApplicationTransactionValidationError(
170
$type,
171
pht('Revoked'),
172
pht(
173
'This key has been revoked. Choose or generate a new, '.
174
'unique key.'),
175
$xaction);
176
continue;
177
}
178
}
179
}
180
break;
181
182
case PhabricatorAuthSSHKeyTransaction::TYPE_DEACTIVATE:
183
foreach ($xactions as $xaction) {
184
if (!$xaction->getNewValue()) {
185
$errors[] = new PhabricatorApplicationTransactionValidationError(
186
$type,
187
pht('Invalid'),
188
pht('SSH keys can not be reactivated.'),
189
$xaction);
190
}
191
}
192
break;
193
}
194
195
return $errors;
196
}
197
198
protected function didCatchDuplicateKeyException(
199
PhabricatorLiskDAO $object,
200
array $xactions,
201
Exception $ex) {
202
203
$errors = array();
204
$errors[] = new PhabricatorApplicationTransactionValidationError(
205
PhabricatorAuthSSHKeyTransaction::TYPE_KEY,
206
pht('Duplicate'),
207
pht(
208
'This public key is already associated with another user or device. '.
209
'Each key must unambiguously identify a single unique owner.'),
210
null);
211
212
throw new PhabricatorApplicationTransactionValidationException($errors);
213
}
214
215
216
protected function shouldSendMail(
217
PhabricatorLiskDAO $object,
218
array $xactions) {
219
return true;
220
}
221
222
protected function getMailSubjectPrefix() {
223
return pht('[SSH Key]');
224
}
225
226
protected function getMailThreadID(PhabricatorLiskDAO $object) {
227
return 'ssh-key-'.$object->getPHID();
228
}
229
230
protected function applyFinalEffects(
231
PhabricatorLiskDAO $object,
232
array $xactions) {
233
234
// After making any change to an SSH key, drop the authfile cache so it
235
// is regenerated the next time anyone authenticates.
236
PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache();
237
238
return $xactions;
239
}
240
241
242
protected function getMailTo(PhabricatorLiskDAO $object) {
243
return $object->getObject()->getSSHKeyNotifyPHIDs();
244
}
245
246
protected function getMailCC(PhabricatorLiskDAO $object) {
247
return array();
248
}
249
250
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
251
return id(new PhabricatorAuthSSHKeyReplyHandler())
252
->setMailReceiver($object);
253
}
254
255
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
256
$id = $object->getID();
257
$name = $object->getName();
258
259
$mail = id(new PhabricatorMetaMTAMail())
260
->setSubject(pht('SSH Key %d: %s', $id, $name));
261
262
// The primary value of this mail is alerting users to account compromises,
263
// so force delivery. In particular, this mail should still be delivered
264
// even if "self mail" is disabled.
265
$mail->setForceDelivery(true);
266
267
return $mail;
268
}
269
270
protected function buildMailBody(
271
PhabricatorLiskDAO $object,
272
array $xactions) {
273
274
$body = parent::buildMailBody($object, $xactions);
275
276
if (!$this->getIsAdministrativeEdit()) {
277
$body->addTextSection(
278
pht('SECURITY WARNING'),
279
pht(
280
'If you do not recognize this change, it may indicate your account '.
281
'has been compromised.'));
282
}
283
284
$detail_uri = $object->getURI();
285
$detail_uri = PhabricatorEnv::getProductionURI($detail_uri);
286
287
$body->addLinkSection(pht('SSH KEY DETAIL'), $detail_uri);
288
289
return $body;
290
}
291
292
293
protected function getCustomWorkerState() {
294
return array(
295
'isAdministrativeEdit' => $this->isAdministrativeEdit,
296
);
297
}
298
299
protected function loadCustomWorkerState(array $state) {
300
$this->isAdministrativeEdit = idx($state, 'isAdministrativeEdit');
301
return $this;
302
}
303
304
305
}
306
307