mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-17 20:32:41 +01:00
Guarantee repositories have unique local paths
Summary: Ref T4039. Long ago these were more freely editable and there were some security concerns around creating a repository, then setting its local path to point somewhere it shouldn't. Local paths are no longer editable so there's no real reason we need to provide a uniqueness guarantee anymore, but you could still make a mistake with `bin/repository move-paths` by accident, and it's a little cleaner to pull them out into their own column with a key. (We still don't -- and, largely can't -- guarantee that two paths aren't //equivalent// since one might be symlinked to the other, or symlinked only on some hosts, or whatever, but the primary value here is as a sanity check that you aren't goofing things up and pointing a bunch of repositories at the same working copy by mistake.) Test Plan: - Ran migrations. - Grepped for `local-path`. - Listed and moved paths with `bin/repository`. - Created a new repository, verified its local path populated correctly. Reviewers: chad Reviewed By: chad Maniphest Tasks: T4039 Differential Revision: https://secure.phabricator.com/D15837
This commit is contained in:
parent
d85386488b
commit
dd2b10b8f8
13 changed files with 89 additions and 37 deletions
2
resources/sql/autopatches/20160503.repo.01.lpath.sql
Normal file
2
resources/sql/autopatches/20160503.repo.01.lpath.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository
|
||||||
|
ADD localPath VARCHAR(128) COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20160503.repo.02.lpathkey.sql
Normal file
2
resources/sql/autopatches/20160503.repo.02.lpathkey.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_repository.repository
|
||||||
|
ADD UNIQUE KEY `key_local` (localPath);
|
57
resources/sql/autopatches/20160503.repo.03.lpathmigrate.php
Normal file
57
resources/sql/autopatches/20160503.repo.03.lpathmigrate.php
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$table = new PhabricatorRepository();
|
||||||
|
$conn_w = $table->establishConnection('w');
|
||||||
|
|
||||||
|
$default_path = PhabricatorEnv::getEnvConfig('repository.default-local-path');
|
||||||
|
$default_path = rtrim($default_path, '/');
|
||||||
|
|
||||||
|
foreach (new LiskMigrationIterator($table) as $repository) {
|
||||||
|
$local_path = $repository->getLocalPath();
|
||||||
|
if (strlen($local_path)) {
|
||||||
|
// Repository already has a modern, unique local path.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$local_path = $repository->getDetail('local-path');
|
||||||
|
if (!strlen($local_path)) {
|
||||||
|
// Repository does not have a local path using the older format.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$random = Filesystem::readRandomCharacters(8);
|
||||||
|
|
||||||
|
// Try the configured path first, then a default path, then a path with some
|
||||||
|
// random noise.
|
||||||
|
$paths = array(
|
||||||
|
$local_path,
|
||||||
|
$default_path.'/'.$repository->getID().'/',
|
||||||
|
$default_path.'/'.$repository->getID().'-'.$random.'/',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($paths as $path) {
|
||||||
|
// Set, then get the path to normalize it.
|
||||||
|
$repository->setLocalPath($path);
|
||||||
|
$path = $repository->getLocalPath();
|
||||||
|
|
||||||
|
try {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET localPath = %s WHERE id = %d',
|
||||||
|
$table->getTableName(),
|
||||||
|
$path,
|
||||||
|
$repository->getID());
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Assigned repository "%s" to local path "%s".',
|
||||||
|
$repository->getDisplayName(),
|
||||||
|
$path));
|
||||||
|
|
||||||
|
break;
|
||||||
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||||
|
// Ignore, try the next one.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -631,9 +631,7 @@ final class DiffusionRepositoryEditMainController
|
||||||
pht('Storage Service'),
|
pht('Storage Service'),
|
||||||
$v_service);
|
$v_service);
|
||||||
|
|
||||||
$view->addProperty(
|
$view->addProperty(pht('Storage Path'), $repository->getLocalPath());
|
||||||
pht('Storage Path'),
|
|
||||||
$repository->getHumanReadableDetail('local-path'));
|
|
||||||
|
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ final class DiffusionRepositoryEditStorageController
|
||||||
|
|
||||||
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
|
||||||
|
|
||||||
$v_local = $repository->getHumanReadableDetail('local-path');
|
$v_local = $repository->getLocalPath();
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
@ -51,11 +51,7 @@ final class DiffusionRepositoryEditStorageController
|
||||||
->appendRemarkupInstructions(
|
->appendRemarkupInstructions(
|
||||||
pht(
|
pht(
|
||||||
"You can not adjust the local path for this repository from the ".
|
"You can not adjust the local path for this repository from the ".
|
||||||
"web interface. To edit it, run this command:\n\n %s",
|
'web interface.'))
|
||||||
sprintf(
|
|
||||||
'phabricator/ $ ./bin/repository edit %s --as %s --local-path ...',
|
|
||||||
$repository->getMonogram(),
|
|
||||||
$viewer->getUsername())))
|
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->addCancelButton($edit_uri, pht('Done')));
|
->addCancelButton($edit_uri, pht('Done')));
|
||||||
|
|
|
@ -40,7 +40,7 @@ final class DiffusionRepositoryStorageManagementPanel
|
||||||
->setViewer($viewer);
|
->setViewer($viewer);
|
||||||
|
|
||||||
if ($repository->usesLocalWorkingCopy()) {
|
if ($repository->usesLocalWorkingCopy()) {
|
||||||
$storage_path = $repository->getHumanReadableDetail('local-path');
|
$storage_path = $repository->getLocalPath();
|
||||||
} else {
|
} else {
|
||||||
$storage_path = phutil_tag('em', array(), pht('No Local Working Copy'));
|
$storage_path = phutil_tag('em', array(), pht('No Local Working Copy'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,6 @@ final class RepositoryCreateConduitAPIMethod
|
||||||
'description' => $request->getValue('description'),
|
'description' => $request->getValue('description'),
|
||||||
'tracking-enabled' => (bool)$request->getValue('tracking', true),
|
'tracking-enabled' => (bool)$request->getValue('tracking', true),
|
||||||
'remote-uri' => $remote_uri,
|
'remote-uri' => $remote_uri,
|
||||||
'local-path' => $local_path,
|
|
||||||
'branch-filter' => array_fill_keys(
|
'branch-filter' => array_fill_keys(
|
||||||
$request->getValue('branchFilter', array()),
|
$request->getValue('branchFilter', array()),
|
||||||
true),
|
true),
|
||||||
|
@ -128,6 +127,8 @@ final class RepositoryCreateConduitAPIMethod
|
||||||
$repository->setDetail($key, $value);
|
$repository->setDetail($key, $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$repository->setLocalPath($local_path);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$repository->save();
|
$repository->save();
|
||||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||||
|
|
|
@ -86,7 +86,7 @@ final class PhabricatorRepositoryEditor
|
||||||
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
|
case PhabricatorRepositoryTransaction::TYPE_REMOTE_URI:
|
||||||
return $object->getDetail('remote-uri');
|
return $object->getDetail('remote-uri');
|
||||||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||||
return $object->getDetail('local-path');
|
return $object->getLocalPath();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||||
return $object->isHosted();
|
return $object->isHosted();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
case PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP:
|
||||||
|
@ -209,7 +209,7 @@ final class PhabricatorRepositoryEditor
|
||||||
$object->setDetail('remote-uri', $xaction->getNewValue());
|
$object->setDetail('remote-uri', $xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
case PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH:
|
||||||
$object->setDetail('local-path', $xaction->getNewValue());
|
$object->setLocalPath($xaction->getNewValue());
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
case PhabricatorRepositoryTransaction::TYPE_HOSTING:
|
||||||
return $object->setHosted($xaction->getNewValue());
|
return $object->setHosted($xaction->getNewValue());
|
||||||
|
@ -706,7 +706,7 @@ final class PhabricatorRepositoryEditor
|
||||||
|
|
||||||
// If the repository does not have a local path yet, assign it one based
|
// If the repository does not have a local path yet, assign it one based
|
||||||
// on its ID. We can't do this earlier because we won't have an ID yet.
|
// on its ID. We can't do this earlier because we won't have an ID yet.
|
||||||
$local_path = $object->getDetail('local-path');
|
$local_path = $object->getLocalPath();
|
||||||
if (!strlen($local_path)) {
|
if (!strlen($local_path)) {
|
||||||
$local_key = 'repository.default-local-path';
|
$local_key = 'repository.default-local-path';
|
||||||
|
|
||||||
|
@ -716,7 +716,7 @@ final class PhabricatorRepositoryEditor
|
||||||
$id = $object->getID();
|
$id = $object->getID();
|
||||||
$local_path = "{$local_root}/{$id}/";
|
$local_path = "{$local_root}/{$id}/";
|
||||||
|
|
||||||
$object->setDetail('local-path', $local_path);
|
$object->setLocalPath($local_path);
|
||||||
$object->save();
|
$object->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ abstract class PhabricatorWorkingCopyTestCase extends PhabricatorTestCase {
|
||||||
->setCallsign($callsign)
|
->setCallsign($callsign)
|
||||||
->setName(pht('Test Repo "%s"', $callsign))
|
->setName(pht('Test Repo "%s"', $callsign))
|
||||||
->setVersionControlSystem($vcs_type)
|
->setVersionControlSystem($vcs_type)
|
||||||
->setDetail('local-path', dirname($local).'/'.$callsign)
|
->setLocalPath(dirname($local).'/'.$callsign)
|
||||||
->setDetail('remote-uri', 'file://'.$dir->getPath().'/');
|
->setDetail('remote-uri', 'file://'.$dir->getPath().'/');
|
||||||
|
|
||||||
$this->didConstructRepository($repo);
|
$this->didConstructRepository($repo);
|
||||||
|
|
|
@ -21,11 +21,6 @@ final class PhabricatorRepositoryManagementEditWorkflow
|
||||||
'param' => 'user',
|
'param' => 'user',
|
||||||
'help' => pht('Edit as user.'),
|
'help' => pht('Edit as user.'),
|
||||||
),
|
),
|
||||||
array(
|
|
||||||
'name' => 'local-path',
|
|
||||||
'param' => 'path',
|
|
||||||
'help' => pht('Edit the local path.'),
|
|
||||||
),
|
|
||||||
array(
|
array(
|
||||||
'name' => 'serve-http',
|
'name' => 'serve-http',
|
||||||
'param' => 'string',
|
'param' => 'string',
|
||||||
|
@ -83,7 +78,6 @@ final class PhabricatorRepositoryManagementEditWorkflow
|
||||||
|
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$type_local_path = PhabricatorRepositoryTransaction::TYPE_LOCAL_PATH;
|
|
||||||
$type_protocol_http =
|
$type_protocol_http =
|
||||||
PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
|
PhabricatorRepositoryTransaction::TYPE_PROTOCOL_HTTP;
|
||||||
$type_protocol_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
|
$type_protocol_ssh = PhabricatorRepositoryTransaction::TYPE_PROTOCOL_SSH;
|
||||||
|
@ -93,11 +87,6 @@ final class PhabricatorRepositoryManagementEditWorkflow
|
||||||
PhabricatorRepository::SERVE_READWRITE,
|
PhabricatorRepository::SERVE_READWRITE,
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($args->getArg('local-path')) {
|
|
||||||
$xactions[] = id(new PhabricatorRepositoryTransaction())
|
|
||||||
->setTransactionType($type_local_path)
|
|
||||||
->setNewValue($args->getArg('local-path'));
|
|
||||||
}
|
|
||||||
$serve_http = $args->getArg('serve-http');
|
$serve_http = $args->getArg('serve-http');
|
||||||
if ($serve_http && in_array($serve_http, $allowed_serve_modes)) {
|
if ($serve_http && in_array($serve_http, $allowed_serve_modes)) {
|
||||||
$xactions[] = id(new PhabricatorRepositoryTransaction())
|
$xactions[] = id(new PhabricatorRepositoryTransaction())
|
||||||
|
|
|
@ -128,14 +128,12 @@ final class PhabricatorRepositoryManagementMovePathsWorkflow
|
||||||
}
|
}
|
||||||
|
|
||||||
$repo = $row['repository'];
|
$repo = $row['repository'];
|
||||||
$details = $repo->getDetails();
|
|
||||||
$details['local-path'] = $row['dst'];
|
|
||||||
|
|
||||||
queryfx(
|
queryfx(
|
||||||
$repo->establishConnection('w'),
|
$repo->establishConnection('w'),
|
||||||
'UPDATE %T SET details = %s WHERE id = %d',
|
'UPDATE %T SET localPath = %s WHERE id = %d',
|
||||||
$repo->getTableName(),
|
$repo->getTableName(),
|
||||||
phutil_json_encode($details),
|
$row['dst'],
|
||||||
$repo->getID());
|
$repo->getID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
protected $credentialPHID;
|
protected $credentialPHID;
|
||||||
protected $almanacServicePHID;
|
protected $almanacServicePHID;
|
||||||
protected $spacePHID;
|
protected $spacePHID;
|
||||||
|
protected $localPath;
|
||||||
|
|
||||||
private $commitCount = self::ATTACHABLE;
|
private $commitCount = self::ATTACHABLE;
|
||||||
private $mostRecentCommit = self::ATTACHABLE;
|
private $mostRecentCommit = self::ATTACHABLE;
|
||||||
|
@ -107,6 +108,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
'pushPolicy' => 'policy',
|
'pushPolicy' => 'policy',
|
||||||
'credentialPHID' => 'phid?',
|
'credentialPHID' => 'phid?',
|
||||||
'almanacServicePHID' => 'phid?',
|
'almanacServicePHID' => 'phid?',
|
||||||
|
'localPath' => 'text128?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'callsign' => array(
|
'callsign' => array(
|
||||||
|
@ -123,6 +125,10 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
'columns' => array('repositorySlug'),
|
'columns' => array('repositorySlug'),
|
||||||
'unique' => true,
|
'unique' => true,
|
||||||
),
|
),
|
||||||
|
'key_local' => array(
|
||||||
|
'columns' => array('localPath'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
@ -216,6 +222,13 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return $monograms;
|
return $monograms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setLocalPath($path) {
|
||||||
|
// Convert any extra slashes ("//") in the path to a single slash ("/").
|
||||||
|
$path = preg_replace('(//+)', '/', $path);
|
||||||
|
|
||||||
|
return parent::setLocalPath($path);
|
||||||
|
}
|
||||||
|
|
||||||
public function getDetail($key, $default = null) {
|
public function getDetail($key, $default = null) {
|
||||||
return idx($this->details, $key, $default);
|
return idx($this->details, $key, $default);
|
||||||
}
|
}
|
||||||
|
@ -279,10 +292,6 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLocalPath() {
|
|
||||||
return $this->getDetail('local-path');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSubversionBaseURI($commit = null) {
|
public function getSubversionBaseURI($commit = null) {
|
||||||
$subpath = $this->getDetail('svn-subpath');
|
$subpath = $this->getDetail('svn-subpath');
|
||||||
if (!strlen($subpath)) {
|
if (!strlen($subpath)) {
|
||||||
|
|
|
@ -70,12 +70,12 @@ final class PhabricatorRepositoryTestCase
|
||||||
|
|
||||||
$repo->setDetail('hosting-enabled', true);
|
$repo->setDetail('hosting-enabled', true);
|
||||||
|
|
||||||
$repo->setDetail('local-path', '/var/repo/SVN');
|
$repo->setLocalPath('/var/repo/SVN');
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'file:///var/repo/SVN',
|
'file:///var/repo/SVN',
|
||||||
$repo->getSubversionPathURI());
|
$repo->getSubversionPathURI());
|
||||||
|
|
||||||
$repo->setDetail('local-path', '/var/repo/SVN/');
|
$repo->setLocalPath('/var/repo/SVN/');
|
||||||
$this->assertEqual(
|
$this->assertEqual(
|
||||||
'file:///var/repo/SVN',
|
'file:///var/repo/SVN',
|
||||||
$repo->getSubversionPathURI());
|
$repo->getSubversionPathURI());
|
||||||
|
|
Loading…
Reference in a new issue