public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $response = $this->loadPropertyObject(); if ($response) { return $response; } $object = $this->getPropertyObject(); $cancel_uri = $object->getURI(); $property_key = $request->getStr('key'); if (!strlen($property_key)) { return $this->buildPropertyKeyResponse($cancel_uri, null); } else { $error = null; try { AlmanacNames::validateName($property_key); } catch (Exception $ex) { $error = $ex->getMessage(); } // NOTE: If you enter an existing name, we're just treating it as an // edit operation. This might be a little confusing. if ($error !== null) { if ($request->isFormPost()) { // The user is creating a new property and picked a bad name. Give // them an opportunity to fix it. return $this->buildPropertyKeyResponse($cancel_uri, $error); } else { // The user is editing an invalid property. return $this->newDialog()->setTitle(pht('Invalid Property'))->appendParagraph(pht('The property name "%s" is invalid. This property can not ' . 'be edited.', $property_key))->appendParagraph($error)->addCancelButton($cancel_uri); } } } return $object->newAlmanacPropertyEditEngine()->addContextParameter('objectPHID')->addContextParameter('key')->setTargetObject($object)->setPropertyKey($property_key)->setController($this)->buildResponse(); }
public function save() { AlmanacNames::validateName($this->getName()); $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); if (!$this->mailKey) { $this->mailKey = Filesystem::readRandomCharacters(20); } return parent::save(); }
public function testServiceOrDeviceNames() { $map = array('' => false, 'a' => false, 'ab' => false, '...' => false, 'ab.' => false, '.ab' => false, 'A-B' => false, 'A!B' => false, 'A.B' => false, 'a..b' => false, '1.2' => false, '127.0.0.1' => false, '1.b' => false, 'a.1' => false, 'a.1.b' => false, '-.a' => false, '-a.b' => false, 'a-.b' => false, 'a.-' => false, 'a.-b' => false, 'a.b-' => false, '-.-' => false, 'a--b' => false, 'abc' => true, 'a.b' => true, 'db.phacility.instance' => true, 'web002.useast.example.com' => true, 'master.example-corp.com' => true, str_repeat('a', 100) => true, str_repeat('a', 101) => false); foreach ($map as $input => $expect) { $caught = null; try { AlmanacNames::validateName($input); } catch (Exception $ex) { $caught = $ex; } $this->assertEqual($expect, !$caught instanceof Exception, $input); } }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacNamespaceTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Namespace name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $name = $xaction->getNewValue(); $message = null; try { AlmanacNames::validateName($name); } catch (Exception $ex) { $message = $ex->getMessage(); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); $errors[] = $error; continue; } $other = id(new AlmanacNamespaceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array($name))->executeOne(); if ($other && $other->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('The namespace name "%s" is already in use by another ' . 'namespace. Each namespace must have a unique name.', $name), $xaction); $errors[] = $error; continue; } if ($name === $object->getName()) { continue; } $namespace = AlmanacNamespace::loadRestrictedNamespace($this->getActor(), $name); if ($namespace) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Restricted'), pht('You do not have permission to create Almanac namespaces ' . 'within the "%s" namespace.', $namespace->getName()), $xaction); $errors[] = $error; continue; } } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacTransaction::TYPE_PROPERTY_UPDATE: foreach ($xactions as $xaction) { $property_key = $xaction->getMetadataValue('almanac.property'); $message = null; try { AlmanacNames::validateName($property_key); } catch (Exception $ex) { $message = $ex->getMessage(); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); $errors[] = $error; continue; } $new_value = $xaction->getNewValue(); try { phutil_json_encode($new_value); } catch (Exception $ex) { $message = pht('Almanac property values must be representable in JSON. %s', $ex->getMessage()); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); $errors[] = $error; continue; } } break; case AlmanacTransaction::TYPE_PROPERTY_REMOVE: // NOTE: No name validation on removals since it's OK to delete // an invalid property that somehow came into existence. break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacServiceTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Service name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $message = null; $name = $xaction->getNewValue(); try { AlmanacNames::validateServiceOrDeviceName($name); } catch (Exception $ex) { $message = $ex->getMessage(); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); $errors[] = $error; } } } if ($xactions) { $duplicate = id(new AlmanacServiceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array(last($xactions)->getNewValue()))->executeOne(); if ($duplicate && $duplicate->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('Almanac services must have unique names.'), last($xactions)); $errors[] = $error; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case AlmanacDeviceTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Device name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } else { foreach ($xactions as $xaction) { $message = null; $name = $xaction->getNewValue(); try { AlmanacNames::validateServiceOrDeviceName($name); } catch (Exception $ex) { $message = $ex->getMessage(); } if ($message !== null) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); $errors[] = $error; } } } if ($xactions) { $duplicate = id(new AlmanacDeviceQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withNames(array(last($xactions)->getNewValue()))->executeOne(); if ($duplicate && $duplicate->getID() != $object->getID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Not Unique'), pht('Almanac devices must have unique names.'), last($xactions)); $errors[] = $error; } } break; case AlmanacDeviceTransaction::TYPE_INTERFACE: // We want to make sure that all the affected networks are visible to // the actor, any edited interfaces exist, and that the actual address // components are valid. $network_phids = array(); foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if ($old) { $network_phids[] = $old['networkPHID']; } if ($new) { $network_phids[] = $new['networkPHID']; $address = $new['address']; if (!strlen($address)) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Interfaces must have an address.'), $xaction); $errors[] = $error; } else { // TODO: Validate addresses, but IPv6 addresses are not trival // to validate. } $port = $new['port']; if (!strlen($port)) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Interfaces must have a port.'), $xaction); $errors[] = $error; } else { if ((int) $port < 1 || (int) $port > 65535) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Port numbers must be between 1 and 65535, inclusive.'), $xaction); $errors[] = $error; } } $phid = idx($new, 'phid'); if ($phid) { $interface_phid_type = AlmanacInterfacePHIDType::TYPECONST; if (phid_get_type($phid) !== $interface_phid_type) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Precomputed interface PHIDs must be of type ' . 'AlmanacInterfacePHIDType.'), $xaction); $errors[] = $error; } } } } if ($network_phids) { $networks = id(new AlmanacNetworkQuery())->setViewer($this->requireActor())->withPHIDs($network_phids)->execute(); $networks = mpull($networks, null, 'getPHID'); } else { $networks = array(); } $addresses = array(); foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); if ($old) { $network = idx($networks, $old['networkPHID']); if (!$network) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not edit an interface which belongs to a ' . 'nonexistent or restricted network.'), $xaction); $errors[] = $error; } $addresses[] = $old['id']; } $new = $xaction->getNewValue(); if ($new) { $network = idx($networks, $new['networkPHID']); if (!$network) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not add an interface on a nonexistent or ' . 'restricted network.'), $xaction); $errors[] = $error; } } } if ($addresses) { $interfaces = id(new AlmanacInterfaceQuery())->setViewer($this->requireActor())->withDevicePHIDs(array($object->getPHID()))->withIDs($addresses)->execute(); $interfaces = mpull($interfaces, null, 'getID'); } else { $interfaces = array(); } foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); if ($old) { $interface = idx($interfaces, $old['id']); if (!$interface) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not edit an invalid or restricted interface.'), $xaction); $errors[] = $error; } } } break; } return $errors; }
public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $object = id(new PhabricatorObjectQuery())->setViewer($viewer)->withPHIDs(array($request->getStr('objectPHID')))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$object) { return new Aphront404Response(); } if (!$object instanceof AlmanacPropertyInterface) { return new Aphront404Response(); } $cancel_uri = $object->getURI(); $key = $request->getStr('key'); if ($key) { $property_key = $key; $is_new = false; $title = pht('Edit Property'); $save_button = pht('Save Changes'); } else { $property_key = null; $is_new = true; $title = pht('Add Property'); $save_button = pht('Add Property'); } if ($is_new) { $errors = array(); $property = null; $v_name = null; $e_name = true; if ($request->isFormPost()) { $name = $request->getStr('name'); if (!strlen($name)) { $e_name = pht('Required'); $errors[] = pht('You must provide a property name.'); } else { $caught = null; try { AlmanacNames::validateServiceOrDeviceName($name); } catch (Exception $ex) { $caught = $ex; } if ($caught) { $e_name = pht('Invalid'); $errors[] = $caught->getMessage(); } } if (!$errors) { $property_key = $name; } } if ($property_key === null) { $form = id(new AphrontFormView())->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setName('name')->setLabel(pht('Name'))->setValue($v_name)->setError($e_name)); return $this->newDialog()->setTitle($title)->setErrors($errors)->addHiddenInput('objectPHID', $request->getStr('objectPHID'))->appendForm($form)->addSubmitButton(pht('Continue'))->addCancelButton($cancel_uri); } } // Make sure property key is appropriate. // TODO: It would be cleaner to put this safety check in the Editor. AlmanacNames::validateServiceOrDeviceName($property_key); // If we're adding a new property, put a placeholder on the object so // that we can build a CustomField for it. if (!$object->hasAlmanacProperty($property_key)) { $temporary_property = id(new AlmanacProperty())->setObjectPHID($object->getPHID())->setFieldName($property_key); $object->attachAlmanacProperties(array($temporary_property)); } $field_list = PhabricatorCustomField::getObjectFields($object, PhabricatorCustomField::ROLE_DEFAULT); // Select only the field being edited. $fields = $field_list->getFields(); $fields = array_select_keys($fields, array($property_key)); $field_list = new PhabricatorCustomFieldList($fields); $field_list->setViewer($viewer)->readFieldsFromStorage($object); $validation_exception = null; if ($request->isFormPost() && $request->getStr('isValueEdit')) { $xactions = $field_list->buildFieldTransactionsFromRequest($object->getApplicationTransactionTemplate(), $request); $editor = $object->getApplicationTransactionEditor()->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); try { $editor->applyTransactions($object, $xactions); return id(new AphrontRedirectResponse())->setURI($cancel_uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; } } $form = id(new AphrontFormView())->setUser($viewer)->addHiddenInput('objectPHID', $request->getStr('objectPHID'))->addHiddenInput('key', $request->getStr('key'))->addHiddenInput('name', $property_key)->addHiddenInput('isValueEdit', true); $field_list->appendFieldsToForm($form); return $this->newDialog()->setTitle($title)->setValidationException($validation_exception)->appendForm($form)->addSubmitButton($save_button)->addCancelButton($cancel_uri); }