Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/passphrase/controller/PassphraseCredentialEditController.php
12256 views
1
<?php
2
3
final class PassphraseCredentialEditController extends PassphraseController {
4
5
public function handleRequest(AphrontRequest $request) {
6
$viewer = $request->getViewer();
7
$id = $request->getURIData('id');
8
9
if ($id) {
10
$credential = id(new PassphraseCredentialQuery())
11
->setViewer($viewer)
12
->withIDs(array($id))
13
->requireCapabilities(
14
array(
15
PhabricatorPolicyCapability::CAN_VIEW,
16
PhabricatorPolicyCapability::CAN_EDIT,
17
))
18
->executeOne();
19
if (!$credential) {
20
return new Aphront404Response();
21
}
22
23
$type = $this->getCredentialType($credential->getCredentialType());
24
$type_const = $type->getCredentialType();
25
26
$is_new = false;
27
} else {
28
$type_const = $request->getStr('type');
29
$type = $this->getCredentialType($type_const);
30
31
if (!$type->isCreateable()) {
32
throw new Exception(
33
pht(
34
'Credential has noncreateable type "%s"!',
35
$type_const));
36
}
37
38
$credential = PassphraseCredential::initializeNewCredential($viewer)
39
->setCredentialType($type->getCredentialType())
40
->setProvidesType($type->getProvidesType())
41
->attachImplementation($type);
42
43
$is_new = true;
44
45
// Prefill username if provided.
46
$credential->setUsername((string)$request->getStr('username'));
47
48
if (!$request->getStr('isInitialized')) {
49
$type->didInitializeNewCredential($viewer, $credential);
50
}
51
}
52
53
$errors = array();
54
55
$v_name = $credential->getName();
56
$e_name = true;
57
58
$v_desc = $credential->getDescription();
59
$v_space = $credential->getSpacePHID();
60
61
$v_username = $credential->getUsername();
62
$e_username = true;
63
64
$v_is_locked = false;
65
66
$bullet = "\xE2\x80\xA2";
67
68
$v_secret = $credential->getSecretID() ? str_repeat($bullet, 32) : null;
69
if ($is_new && ($v_secret === null)) {
70
// If we're creating a new credential, the credential type may have
71
// populated the secret for us (for example, generated an SSH key). In
72
// this case,
73
try {
74
$v_secret = $credential->getSecret()->openEnvelope();
75
} catch (Exception $ex) {
76
// Ignore this.
77
}
78
}
79
80
$validation_exception = null;
81
$errors = array();
82
$e_password = null;
83
$e_secret = null;
84
if ($request->isFormPost()) {
85
86
$v_name = $request->getStr('name');
87
$v_desc = $request->getStr('description');
88
$v_username = $request->getStr('username');
89
$v_view_policy = $request->getStr('viewPolicy');
90
$v_edit_policy = $request->getStr('editPolicy');
91
$v_is_locked = $request->getStr('lock');
92
93
$v_secret = $request->getStr('secret');
94
$v_space = $request->getStr('spacePHID');
95
$v_password = $request->getStr('password');
96
$v_decrypt = $v_secret;
97
98
$env_secret = new PhutilOpaqueEnvelope($v_secret);
99
$env_password = new PhutilOpaqueEnvelope($v_password);
100
101
$has_secret = !preg_match('/^('.$bullet.')+$/', trim($v_decrypt));
102
103
// Validate and repair SSH private keys, and apply passwords if they
104
// are provided. See T13454 for discussion.
105
106
// This should eventually be refactored to be modular rather than a
107
// hard-coded set of behaviors here in the Controller, but this is
108
// likely a fairly extensive change.
109
110
$is_ssh = ($type instanceof PassphraseSSHPrivateKeyTextCredentialType);
111
112
if ($is_ssh && $has_secret) {
113
$old_object = PhabricatorAuthSSHPrivateKey::newFromRawKey($env_secret);
114
115
if (strlen($v_password)) {
116
$old_object->setPassphrase($env_password);
117
}
118
119
try {
120
$new_object = $old_object->newBarePrivateKey();
121
$v_decrypt = $new_object->getKeyBody()->openEnvelope();
122
} catch (PhabricatorAuthSSHPrivateKeyException $ex) {
123
$errors[] = $ex->getMessage();
124
125
if ($ex->isFormatException()) {
126
$e_secret = pht('Invalid');
127
}
128
if ($ex->isPassphraseException()) {
129
$e_password = pht('Invalid');
130
}
131
}
132
}
133
134
if (!$errors) {
135
$type_name =
136
PassphraseCredentialNameTransaction::TRANSACTIONTYPE;
137
$type_desc =
138
PassphraseCredentialDescriptionTransaction::TRANSACTIONTYPE;
139
$type_username =
140
PassphraseCredentialUsernameTransaction::TRANSACTIONTYPE;
141
$type_destroy =
142
PassphraseCredentialDestroyTransaction::TRANSACTIONTYPE;
143
$type_secret_id =
144
PassphraseCredentialSecretIDTransaction::TRANSACTIONTYPE;
145
$type_is_locked =
146
PassphraseCredentialLockTransaction::TRANSACTIONTYPE;
147
148
$type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY;
149
$type_edit_policy = PhabricatorTransactions::TYPE_EDIT_POLICY;
150
$type_space = PhabricatorTransactions::TYPE_SPACE;
151
152
$xactions = array();
153
154
$xactions[] = id(new PassphraseCredentialTransaction())
155
->setTransactionType($type_name)
156
->setNewValue($v_name);
157
158
$xactions[] = id(new PassphraseCredentialTransaction())
159
->setTransactionType($type_desc)
160
->setNewValue($v_desc);
161
162
$xactions[] = id(new PassphraseCredentialTransaction())
163
->setTransactionType($type_view_policy)
164
->setNewValue($v_view_policy);
165
166
$xactions[] = id(new PassphraseCredentialTransaction())
167
->setTransactionType($type_edit_policy)
168
->setNewValue($v_edit_policy);
169
170
$xactions[] = id(new PassphraseCredentialTransaction())
171
->setTransactionType($type_space)
172
->setNewValue($v_space);
173
174
// Open a transaction in case we're writing a new secret; this limits
175
// the amount of code which handles secret plaintexts.
176
$credential->openTransaction();
177
178
if (!$credential->getIsLocked()) {
179
if ($type->shouldRequireUsername()) {
180
$xactions[] = id(new PassphraseCredentialTransaction())
181
->setTransactionType($type_username)
182
->setNewValue($v_username);
183
}
184
185
// If some value other than a sequence of bullets was provided for
186
// the credential, update it. In particular, note that we are
187
// explicitly allowing empty secrets: one use case is HTTP auth where
188
// the username is a secret token which covers both identity and
189
// authentication.
190
191
if ($has_secret) {
192
// If the credential was previously destroyed, restore it when it is
193
// edited if a secret is provided.
194
$xactions[] = id(new PassphraseCredentialTransaction())
195
->setTransactionType($type_destroy)
196
->setNewValue(0);
197
198
$new_secret = id(new PassphraseSecret())
199
->setSecretData($v_decrypt)
200
->save();
201
202
$xactions[] = id(new PassphraseCredentialTransaction())
203
->setTransactionType($type_secret_id)
204
->setNewValue($new_secret->getID());
205
}
206
207
$xactions[] = id(new PassphraseCredentialTransaction())
208
->setTransactionType($type_is_locked)
209
->setNewValue($v_is_locked);
210
}
211
212
try {
213
$editor = id(new PassphraseCredentialTransactionEditor())
214
->setActor($viewer)
215
->setContinueOnNoEffect(true)
216
->setContentSourceFromRequest($request)
217
->applyTransactions($credential, $xactions);
218
219
$credential->saveTransaction();
220
221
if ($request->isAjax()) {
222
return id(new AphrontAjaxResponse())->setContent(
223
array(
224
'phid' => $credential->getPHID(),
225
'name' => 'K'.$credential->getID().' '.$credential->getName(),
226
));
227
} else {
228
return id(new AphrontRedirectResponse())
229
->setURI('/K'.$credential->getID());
230
}
231
} catch (PhabricatorApplicationTransactionValidationException $ex) {
232
$credential->killTransaction();
233
234
$validation_exception = $ex;
235
236
$e_name = $ex->getShortMessage($type_name);
237
$e_username = $ex->getShortMessage($type_username);
238
239
$credential->setViewPolicy($v_view_policy);
240
$credential->setEditPolicy($v_edit_policy);
241
}
242
}
243
}
244
245
$policies = id(new PhabricatorPolicyQuery())
246
->setViewer($viewer)
247
->setObject($credential)
248
->execute();
249
250
$secret_control = $type->newSecretControl();
251
$credential_is_locked = $credential->getIsLocked();
252
253
$form = id(new AphrontFormView())
254
->setUser($viewer)
255
->addHiddenInput('isInitialized', true)
256
->addHiddenInput('type', $type_const)
257
->appendChild(
258
id(new AphrontFormTextControl())
259
->setName('name')
260
->setLabel(pht('Name'))
261
->setValue($v_name)
262
->setError($e_name))
263
->appendChild(
264
id(new PhabricatorRemarkupControl())
265
->setUser($viewer)
266
->setName('description')
267
->setLabel(pht('Description'))
268
->setValue($v_desc))
269
->appendChild(
270
id(new AphrontFormDividerControl()))
271
->appendControl(
272
id(new AphrontFormPolicyControl())
273
->setName('viewPolicy')
274
->setPolicyObject($credential)
275
->setSpacePHID($v_space)
276
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
277
->setPolicies($policies))
278
->appendControl(
279
id(new AphrontFormPolicyControl())
280
->setName('editPolicy')
281
->setPolicyObject($credential)
282
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
283
->setPolicies($policies))
284
->appendChild(
285
id(new AphrontFormDividerControl()));
286
287
if ($credential_is_locked) {
288
$form->appendRemarkupInstructions(
289
pht('This credential is permanently locked and can not be edited.'));
290
}
291
292
if ($type->shouldRequireUsername()) {
293
$form->appendChild(
294
id(new AphrontFormTextControl())
295
->setName('username')
296
->setLabel(pht('Login/Username'))
297
->setValue($v_username)
298
->setDisabled($credential_is_locked)
299
->setError($e_username));
300
}
301
302
$form->appendChild(
303
$secret_control
304
->setName('secret')
305
->setLabel($type->getSecretLabel())
306
->setDisabled($credential_is_locked)
307
->setValue($v_secret)
308
->setError($e_secret));
309
310
if ($type->shouldShowPasswordField()) {
311
$form->appendChild(
312
id(new AphrontFormPasswordControl())
313
->setDisableAutocomplete(true)
314
->setName('password')
315
->setLabel($type->getPasswordLabel())
316
->setDisabled($credential_is_locked)
317
->setError($e_password));
318
}
319
320
if ($is_new) {
321
$form->appendChild(
322
id(new AphrontFormCheckboxControl())
323
->addCheckbox(
324
'lock',
325
1,
326
array(
327
phutil_tag('strong', array(), pht('Lock Permanently:')),
328
' ',
329
pht('Prevent the secret from being revealed or changed.'),
330
),
331
$v_is_locked)
332
->setDisabled($credential_is_locked));
333
}
334
335
$crumbs = $this->buildApplicationCrumbs();
336
$crumbs->setBorder(true);
337
338
if ($is_new) {
339
$title = pht('New Credential: %s', $type->getCredentialTypeName());
340
$crumbs->addTextCrumb(pht('Create'));
341
$cancel_uri = $this->getApplicationURI();
342
} else {
343
$title = pht('Edit Credential: %s', $credential->getName());
344
$crumbs->addTextCrumb(
345
'K'.$credential->getID(),
346
'/K'.$credential->getID());
347
$crumbs->addTextCrumb(pht('Edit'));
348
$cancel_uri = '/K'.$credential->getID();
349
}
350
351
if ($request->isAjax()) {
352
if ($errors) {
353
$errors = id(new PHUIInfoView())->setErrors($errors);
354
}
355
356
return $this->newDialog()
357
->setWidth(AphrontDialogView::WIDTH_FORM)
358
->setTitle($title)
359
->appendChild($errors)
360
->appendChild($form->buildLayoutView())
361
->addSubmitButton(pht('Create Credential'))
362
->addCancelButton($cancel_uri);
363
}
364
365
$form->appendChild(
366
id(new AphrontFormSubmitControl())
367
->setValue(pht('Save'))
368
->addCancelButton($cancel_uri));
369
370
$box = id(new PHUIObjectBoxView())
371
->setHeaderText($title)
372
->setFormErrors($errors)
373
->setValidationException($validation_exception)
374
->setBackground(PHUIObjectBoxView::WHITE_CONFIG)
375
->setForm($form);
376
377
$view = id(new PHUITwoColumnView())
378
->setFooter(array(
379
$box,
380
));
381
382
return $this->newPage()
383
->setTitle($title)
384
->setCrumbs($crumbs)
385
->appendChild($view);
386
}
387
388
private function getCredentialType($type_const) {
389
$type = PassphraseCredentialType::getTypeByConstant($type_const);
390
391
if (!$type) {
392
throw new Exception(
393
pht('Credential has invalid type "%s"!', $type_const));
394
}
395
396
return $type;
397
}
398
399
}
400
401