protected function execute(ConduitAPIRequest $request) { $application = id(new PhabricatorApplicationQuery())->setViewer($request->getUser())->withClasses(array('PhabricatorDiffusionApplication'))->executeOne(); PhabricatorPolicyFilter::requireCapability($request->getUser(), $application, DiffusionCreateRepositoriesCapability::CAPABILITY); // TODO: This has some duplication with (and lacks some of the validation // of) the web workflow; refactor things so they can share more code as this // stabilizes. Specifically, this should move to transactions since they // work properly now. $repository = PhabricatorRepository::initializeNewRepository($request->getUser()); $repository->setName($request->getValue('name')); $callsign = $request->getValue('callsign'); if (!preg_match('/^[A-Z]+\\z/', $callsign)) { throw new ConduitException('ERR-BAD-CALLSIGN'); } $repository->setCallsign($callsign); $local_path = PhabricatorEnv::getEnvConfig('repository.default-local-path'); $local_path = rtrim($local_path, '/'); $local_path = $local_path . '/' . $callsign . '/'; $vcs = $request->getValue('vcs'); $map = array('git' => PhabricatorRepositoryType::REPOSITORY_TYPE_GIT, 'hg' => PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL, 'svn' => PhabricatorRepositoryType::REPOSITORY_TYPE_SVN); if (empty($map[$vcs])) { throw new ConduitException('ERR-UNKNOWN-REPOSITORY-VCS'); } $repository->setVersionControlSystem($map[$vcs]); $repository->setCredentialPHID($request->getValue('credentialPHID')); $remote_uri = $request->getValue('uri'); PhabricatorRepository::assertValidRemoteURI($remote_uri); $details = array('encoding' => $request->getValue('encoding'), 'description' => $request->getValue('description'), 'tracking-enabled' => (bool) $request->getValue('tracking', true), 'remote-uri' => $remote_uri, 'local-path' => $local_path, 'branch-filter' => array_fill_keys($request->getValue('branchFilter', array()), true), 'close-commits-filter' => array_fill_keys($request->getValue('closeCommitsFilter', array()), true), 'pull-frequency' => $request->getValue('pullFrequency'), 'default-branch' => $request->getValue('defaultBranch'), 'herald-disabled' => !$request->getValue('heraldEnabled', true), 'svn-subpath' => $request->getValue('svnSubpath'), 'disable-autoclose' => !$request->getValue('autocloseEnabled', true)); foreach ($details as $key => $value) { $repository->setDetail($key, $value); } try { $repository->save(); } catch (AphrontDuplicateKeyQueryException $ex) { throw new ConduitException('ERR-DUPLICATE'); } return $repository->toDictionary(); }
public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $drequest = $this->diffusionRequest; $repository = $drequest->getRepository(); if ($this->id) { $mirror = id(new PhabricatorRepositoryMirrorQuery())->setViewer($viewer)->withIDs(array($this->id))->executeOne(); if (!$mirror) { return new Aphront404Response(); } $is_new = false; } else { $mirror = PhabricatorRepositoryMirror::initializeNewMirror($viewer)->setRepositoryPHID($repository->getPHID())->attachRepository($repository); $is_new = true; } $edit_uri = $this->getRepositoryControllerURI($repository, 'edit/#mirrors'); $v_remote = $mirror->getRemoteURI(); $e_remote = true; $v_credentials = $mirror->getCredentialPHID(); $e_credentials = null; $credentials = id(new PassphraseCredentialQuery())->setViewer($viewer)->withIsDestroyed(false)->execute(); $errors = array(); if ($request->isFormPost()) { $v_remote = $request->getStr('remoteURI'); if (strlen($v_remote)) { try { PhabricatorRepository::assertValidRemoteURI($v_remote); $e_remote = null; } catch (Exception $ex) { $e_remote = pht('Invalid'); $errors[] = $ex->getMessage(); } } else { $e_remote = pht('Required'); $errors[] = pht('You must provide a remote URI.'); } $v_credentials = $request->getStr('credential'); if ($v_credentials) { $phids = mpull($credentials, null, 'getPHID'); if (empty($phids[$v_credentials])) { $e_credentials = pht('Invalid'); $errors[] = pht('You do not have permission to use those credentials.'); } } if (!$errors) { $mirror->setRemoteURI($v_remote)->setCredentialPHID($v_credentials)->save(); return id(new AphrontReloadResponse())->setURI($edit_uri); } } $form_errors = null; if ($errors) { $form_errors = id(new AphrontErrorView())->setErrors($errors); } if ($is_new) { $title = pht('Create Mirror'); $submit = pht('Create Mirror'); } else { $title = pht('Edit Mirror'); $submit = pht('Save Changes'); } $form = id(new PHUIFormLayoutView())->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Remote URI'))->setName('remoteURI')->setValue($v_remote)->setError($e_remote))->appendChild(id(new PassphraseCredentialControl())->setLabel(pht('Credentials'))->setName('credential')->setAllowNull(true)->setValue($v_credentials)->setError($e_credentials)->setOptions($credentials)); $dialog = id(new AphrontDialogView())->setUser($viewer)->setTitle($title)->setWidth(AphrontDialogView::WIDTH_FORM)->appendChild($form_errors)->appendChild($form)->addSubmitButton($submit)->addCancelButton($edit_uri); return id(new AphrontDialogResponse())->setDialog($dialog); }
public function validateRemoteURIPage(PHUIFormPageView $page) { $c_remote = $page->getControl('remoteURI'); $v_remote = $c_remote->getValue(); if (!strlen($v_remote)) { $c_remote->setError(pht('Required')); $page->addPageError(pht('You must specify a URI.')); } else { try { PhabricatorRepository::assertValidRemoteURI($v_remote); } catch (Exception $ex) { $c_remote->setError(pht('Invalid')); $page->addPageError($ex->getMessage()); } } return $c_remote->isValid(); }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: foreach ($xactions as $xaction) { foreach ($xaction->getNewValue() as $pattern) { // Check for invalid regular expressions. $regexp = PhabricatorRepository::extractBranchRegexp($pattern); if ($regexp !== null) { $ok = @preg_match($regexp, ''); if ($ok === false) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Expression "%s" is not a valid regular expression. Note ' . 'that you must include delimiters.', $regexp), $xaction); $errors[] = $error; continue; } } // Check for formatting mistakes like `regex(...)` instead of // `regexp(...)`. $matches = null; if (preg_match('/^([^(]+)\\(.*\\)\\z/', $pattern, $matches)) { switch ($matches[1]) { case 'regexp': break; default: $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Matching function "%s(...)" is not recognized. Valid ' . 'functions are: regexp(...).', $matches[1]), $xaction); $errors[] = $error; break; } } } } break; case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: foreach ($xactions as $xaction) { $new_uri = $xaction->getNewValue(); try { PhabricatorRepository::assertValidRemoteURI($new_uri); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } break; case PhabricatorRepositoryTransaction::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; case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS: foreach ($xactions as $xaction) { $old = nonempty($xaction->getOldValue(), array()); $new = nonempty($xaction->getNewValue(), array()); $add = array_diff($new, $old); $invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer($this->getActor(), $add); if ($invalid) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Some of the selected automation blueprints are invalid ' . 'or restricted: %s.', implode(', ', $invalid)), $xaction); } } break; case PhabricatorRepositoryTransaction::TYPE_SLUG: foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if (!strlen($new)) { continue; } if ($new === $old) { continue; } try { PhabricatorRepository::asssertValidRepositorySlug($new); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); continue; } $other = id(new PhabricatorRepositoryQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withSlugs(array($new))->executeOne(); if ($other && $other->getID() !== $object->getID()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Duplicate'), pht('The selected repository short name is already in use by ' . 'another repository. Choose a unique short name.'), $xaction); continue; } } break; } return $errors; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE: case PhabricatorRepositoryTransaction::TYPE_TRACK_ONLY: foreach ($xactions as $xaction) { foreach ($xaction->getNewValue() as $pattern) { // Check for invalid regular expressions. $regexp = PhabricatorRepository::extractBranchRegexp($pattern); if ($regexp !== null) { $ok = @preg_match($regexp, ''); if ($ok === false) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Expression "%s" is not a valid regular expression. Note ' . 'that you must include delimiters.', $regexp), $xaction); $errors[] = $error; continue; } } // Check for formatting mistakes like `regex(...)` instead of // `regexp(...)`. $matches = null; if (preg_match('/^([^(]+)\\(.*\\)\\z/', $pattern, $matches)) { switch ($matches[1]) { case 'regexp': break; default: $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Matching function "%s(...)" is not recognized. Valid ' . 'functions are: regexp(...).', $matches[1]), $xaction); $errors[] = $error; break; } } } } break; case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI: foreach ($xactions as $xaction) { $new_uri = $xaction->getNewValue(); try { PhabricatorRepository::assertValidRemoteURI($new_uri); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); } } break; case PhabricatorRepositoryTransaction::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; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorRepositoryURITransaction::TYPE_REPOSITORY: // Save this, since we need it to validate TYPE_IO transactions. $this->repositoryPHID = $object->getRepositoryPHID(); $missing = $this->validateIsEmptyTextField($object->getRepositoryPHID(), $xactions); if ($missing) { // NOTE: This isn't being marked as a missing field error because // it's a fundamental, required property of the URI. $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('When creating a repository URI, you must specify which ' . 'repository the URI will belong to.'), nonempty(last($xactions), null)); break; } $viewer = $this->getActor(); foreach ($xactions as $xaction) { $repository_phid = $xaction->getNewValue(); // If this isn't changing anything, let it through as-is. if ($repository_phid == $object->getRepositoryPHID()) { continue; } if (!$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('The repository a URI is associated with is immutable, and ' . 'can not be changed after the URI is created.'), $xaction); continue; } $repository = id(new PhabricatorRepositoryQuery())->setViewer($viewer)->withPHIDs(array($repository_phid))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$repository) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('To create a URI for a repository ("%s"), it must exist and ' . 'you must have permission to edit it.', $repository_phid), $xaction); continue; } $this->repository = $repository; $this->repositoryPHID = $repository_phid; } break; case PhabricatorRepositoryURITransaction::TYPE_CREDENTIAL: $viewer = $this->getActor(); foreach ($xactions as $xaction) { $credential_phid = $xaction->getNewValue(); if ($credential_phid == $object->getCredentialPHID()) { continue; } // Anyone who can edit a URI can remove the credential. if ($credential_phid === null) { continue; } $credential = id(new PassphraseCredentialQuery())->setViewer($viewer)->withPHIDs(array($credential_phid))->executeOne(); if (!$credential) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only associate a credential ("%s") with a repository ' . 'URI if it exists and you have permission to see it.', $credential_phid), $xaction); continue; } } break; case PhabricatorRepositoryURITransaction::TYPE_URI: $missing = $this->validateIsEmptyTextField($object->getURI(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('A repository URI must have a nonempty URI.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; break; } foreach ($xactions as $xaction) { $new_uri = $xaction->getNewValue(); if ($new_uri == $object->getURI()) { continue; } try { PhabricatorRepository::assertValidRemoteURI($new_uri); } catch (Exception $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $ex->getMessage(), $xaction); continue; } } break; case PhabricatorRepositoryURITransaction::TYPE_IO: $available = $object->getAvailableIOTypeOptions(); foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); if (empty($available[$new])) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Value "%s" is not a valid display setting for this URI. ' . 'Available types for this URI are: %s.', implode(', ', array_keys($available))), $xaction); continue; } // If we are setting this URI to use "Observe", we must have no // other "Observe" URIs and must also have no "Read/Write" URIs. // If we are setting this URI to "Read/Write", we must have no // other "Observe" URIs. It's OK to have other "Read/Write" URIs. $no_observers = false; $no_readwrite = false; switch ($new) { case PhabricatorRepositoryURI::IO_OBSERVE: $no_readwrite = true; $no_observers = true; break; case PhabricatorRepositoryURI::IO_READWRITE: $no_observers = true; break; } if ($no_observers || $no_readwrite) { $repository = id(new PhabricatorRepositoryQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs(array($this->repositoryPHID))->needURIs(true)->executeOne(); $uris = $repository->getURIs(); $observe_conflict = null; $readwrite_conflict = null; foreach ($uris as $uri) { // If this is the URI being edited, it can not conflict with // itself. if ($uri->getID() == $object->getID()) { continue; } $io_type = $uri->getIoType(); if ($io_type == PhabricatorRepositoryURI::IO_READWRITE) { if ($no_readwrite) { $readwite_conflict = $uri; break; } } if ($io_type == PhabricatorRepositoryURI::IO_OBSERVE) { if ($no_observers) { $observe_conflict = $uri; break; } } } if ($observe_conflict) { if ($new == PhabricatorRepositoryURI::IO_OBSERVE) { $message = pht('You can not set this URI to use Observe IO because ' . 'another URI for this repository is already configured ' . 'in Observe IO mode. A repository can not observe two ' . 'different remotes simultaneously. Turn off IO for the ' . 'other URI first.'); } else { $message = pht('You can not set this URI to use Read/Write IO because ' . 'another URI for this repository is already configured ' . 'in Observe IO mode. An observed repository can not be ' . 'made writable. Turn off IO for the other URI first.'); } $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); continue; } if ($readwrite_conflict) { $message = pht('You can not set this URI to use Observe IO because ' . 'another URI for this repository is already configured ' . 'in Read/Write IO mode. A repository can not simultaneously ' . 'be writable and observe a remote. Turn off IO for the ' . 'other URI first.'); $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), $message, $xaction); continue; } } } break; case PhabricatorRepositoryURITransaction::TYPE_DISPLAY: $available = $object->getAvailableDisplayTypeOptions(); foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); if (empty($available[$new])) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Value "%s" is not a valid display setting for this URI. ' . 'Available types for this URI are: %s.', implode(', ', array_keys($available)))); } } break; case PhabricatorRepositoryURITransaction::TYPE_DISABLE: $old = $object->getIsDisabled(); foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); if ($old == $new) { continue; } if (!$object->isBuiltin()) { continue; } $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not manually disable builtin URIs.')); } break; } return $errors; }