1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-01 01:18:22 +01:00

Implement a "credential" standard custom field

Summary: Ref T4590. Ref T1049. This is primarily intended to support HTTP auth in Harbormaster.

Test Plan: Added a field, edited it, etc.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4590, T1049

Differential Revision: https://secure.phabricator.com/D8607
This commit is contained in:
epriestley 2014-03-25 16:13:27 -07:00
parent acfc3c3e5d
commit e8e12910a7
7 changed files with 245 additions and 1 deletions

View file

@ -2091,6 +2091,7 @@ phutil_register_library_map(array(
'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php',
'PhabricatorStandardCustomField' => 'infrastructure/customfield/standard/PhabricatorStandardCustomField.php',
'PhabricatorStandardCustomFieldBool' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldBool.php',
'PhabricatorStandardCustomFieldCredential' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldCredential.php',
'PhabricatorStandardCustomFieldDate' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldDate.php',
'PhabricatorStandardCustomFieldHeader' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldHeader.php',
'PhabricatorStandardCustomFieldInt' => 'infrastructure/customfield/standard/PhabricatorStandardCustomFieldInt.php',
@ -4937,6 +4938,7 @@ phutil_register_library_map(array(
'PhabricatorSourceCodeView' => 'AphrontView',
'PhabricatorStandardCustomField' => 'PhabricatorCustomField',
'PhabricatorStandardCustomFieldBool' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldCredential' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldDate' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldHeader' => 'PhabricatorStandardCustomField',
'PhabricatorStandardCustomFieldInt' => 'PhabricatorStandardCustomField',

View file

@ -33,6 +33,14 @@ abstract class PassphraseCredentialType extends Phobject {
return $types;
}
public static function getAllProvidesTypes() {
$types = array();
foreach (self::getAllTypes() as $type) {
$types[] = $type->getProvidesType();
}
return array_unique($types);
}
public static function getTypeByConstant($constant) {
$all = self::getAllTypes();
$all = mpull($all, null, 'getCredentialType');

View file

@ -98,4 +98,63 @@ final class PassphraseCredentialControl extends AphrontFormControl {
));
}
/**
* Verify that a given actor has permission to use all of the credentials
* in a list of credential transactions.
*
* In general, the rule here is:
*
* - If you're editing an object and it uses a credential you can't use,
* that's fine as long as you don't change the credential.
* - If you do change the credential, the new credential must be one you
* can use.
*
* @param PhabricatorUser The acting user.
* @param list<PhabricatorApplicationTransaction> List of credential altering
* transactions.
* @return bool True if the transactions are valid.
*/
public static function validateTransactions(
PhabricatorUser $actor,
array $xactions) {
$new_phids = array();
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
if (!$new) {
// Removing a credential, so this is OK.
continue;
}
$old = $xaction->getOldValue();
if ($old == $new) {
// This is a no-op transaction, so this is also OK.
continue;
}
// Otherwise, we need to check this credential.
$new_phids[] = $new;
}
if (!$new_phids) {
// No new credentials being set, so this is fine.
return true;
}
$usable_credentials = id(new PassphraseCredentialQuery())
->setViewer($actor)
->withPHIDs($new_phids)
->execute();
$usable_credentials = mpull($usable_credentials, null, 'getPHID');
foreach ($new_phids as $phid) {
if (empty($usable_credentials[$phid])) {
return false;
}
}
return true;
}
}

View file

@ -310,4 +310,33 @@ final class PhabricatorRepositoryEditor
}
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case self::TYPE_CREDENTIAL:
$ok = PassphraseCredentialControl::validateTransactions(
$this->getActor(),
$xactions);
if (!$ok) {
foreach ($xactions as $xaction) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'The selected credential does not exist, or you do not have '.
'permission to use it.'),
$xaction);
}
}
break;
}
return $errors;
}
}

View file

@ -121,6 +121,14 @@ In **Maniphest**:
into the new task, while others are not; by default, fields are not copied.
If you want this field to be copied, specify `true` for the `copy` property.
Internally, Phabricator implements some additional custom field types and
options. These are not intended for general use and are subject to abrupt
change, but are documented here for completeness:
- **Credentials**: Controls with type `credential` allow selection of a
Passphrase credential which provides `credential.provides`, and creation
of credentials of `credential.type`.
= Advanced Custom Fields =
If you want custom fields to have advanced behaviors (sophisticated rendering,

View file

@ -0,0 +1,138 @@
<?php
final class PhabricatorStandardCustomFieldCredential
extends PhabricatorStandardCustomField {
public function getFieldType() {
return 'credential';
}
public function buildFieldIndexes() {
$indexes = array();
$value = $this->getFieldValue();
if (strlen($value)) {
$indexes[] = $this->newStringIndex($value);
}
return $indexes;
}
public function renderEditControl(array $handles) {
$provides_type = $this->getFieldConfigValue('credential.provides');
$credential_type = $this->getFieldConfigValue('credential.type');
$all_types = PassphraseCredentialType::getAllProvidesTypes();
if (!in_array($provides_type, $all_types)) {
$provides_type = PassphraseCredentialTypePassword::PROVIDES_TYPE;
}
$credentials = id(new PassphraseCredentialQuery())
->setViewer($this->getViewer())
->withIsDestroyed(false)
->withProvidesTypes(array($provides_type))
->execute();
return id(new PassphraseCredentialControl())
->setLabel($this->getFieldName())
->setName($this->getFieldKey())
->setCaption($this->getCaption())
->setAllowNull(!$this->getRequired())
->setCredentialType($credential_type)
->setValue($this->getFieldValue())
->setError($this->getFieldError())
->setOptions($credentials);
}
public function getRequiredHandlePHIDsForPropertyView() {
$value = $this->getFieldValue();
if ($value) {
return array($value);
}
return array();
}
public function renderPropertyViewValue(array $handles) {
$value = $this->getFieldValue();
if ($value) {
return $handles[$value]->renderLink();
}
return null;
}
public function validateApplicationTransactions(
PhabricatorApplicationTransactionEditor $editor,
$type,
array $xactions) {
$errors = parent::validateApplicationTransactions(
$editor,
$type,
$xactions);
$ok = PassphraseCredentialControl::validateTransactions(
$this->getViewer(),
$xactions);
if (!$ok) {
foreach ($xactions as $xaction) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'The selected credential does not exist, or you do not have '.
'permission to use it.'),
$xaction);
$this->setFieldError(pht('Invalid'));
}
}
return $errors;
}
public function getApplicationTransactionRequiredHandlePHIDs(
PhabricatorApplicationTransaction $xaction) {
$phids = array();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
return $phids;
}
public function getApplicationTransactionTitle(
PhabricatorApplicationTransaction $xaction) {
$author_phid = $xaction->getAuthorPHID();
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
if ($old && !$new) {
return pht(
'%s removed %s as %s.',
$xaction->renderHandleLink($author_phid),
$xaction->renderHandleLink($old),
$this->getFieldName());
} else if ($new && !$old) {
return pht(
'%s set %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($new));
} else {
return pht(
'%s changed %s from %s to %s.',
$xaction->renderHandleLink($author_phid),
$this->getFieldName(),
$xaction->renderHandleLink($old),
$xaction->renderHandleLink($new));
}
}
}

View file

@ -68,7 +68,7 @@ abstract class PhabricatorStandardCustomFieldPHIDs
return array();
}
public function getRequiredHandlePHIDsForProperyView() {
public function getRequiredHandlePHIDsForPropertyView() {
$value = $this->getFieldValue();
if ($value) {
return $value;