1
0
Fork 0

Merge pull request #1177 from sandermarechal/stream-context

Allow setting stream context options
pull/1182/merge
Jordi Boggiano 2012-10-04 09:18:08 -07:00
commit b3077bc4bc
6 changed files with 92 additions and 7 deletions

View File

@ -504,8 +504,10 @@ ignored.
The following repository types are supported: The following repository types are supported:
* **composer:** A composer repository is simply a `packages.json` file served * **composer:** A composer repository is simply a `packages.json` file served
via HTTP, that contains a list of `composer.json` objects with additional via the network (HTTP, FTP, SSH), that contains a list of `composer.json`
`dist` and/or `source` information. objects with additional `dist` and/or `source` information. The `packages.json`
file is loaded using a PHP stream. You can set extra options on that stream
using the `options` parameter.
* **vcs:** The version control system repository can fetch packages from git, * **vcs:** The version control system repository can fetch packages from git,
svn and hg repositories. svn and hg repositories.
* **pear:** With this you can import any pear repository into your composer * **pear:** With this you can import any pear repository into your composer
@ -524,6 +526,15 @@ Example:
"type": "composer", "type": "composer",
"url": "http://packages.example.com" "url": "http://packages.example.com"
}, },
{
"type": "composer",
"url": "https://packages.example.com",
"options": {
"ssl": {
"verify_peer": "true"
}
}
},
{ {
"type": "vcs", "type": "vcs",
"url": "https://github.com/Seldaek/monolog" "url": "https://github.com/Seldaek/monolog"

View File

@ -148,6 +148,13 @@ hash changed.
This field is optional. You probably don't need it for your own custom This field is optional. You probably don't need it for your own custom
repository. repository.
#### stream options
The `packages.json` file is loaded using a PHP stream. You can set extra options
on that stream using the `options` parameter. You can set any valid PHP stream
context option. See [Context options and parameters](http://nl3.php.net/manual/en/context.php)
for more information.
### VCS ### VCS
VCS stands for version control system. This includes versioning systems like VCS stands for version control system. This includes versioning systems like

View File

@ -85,3 +85,43 @@ itself.
"company/package3": "dev-master" "company/package3": "dev-master"
} }
} }
### Security
To secure your private repository you can host it over SSH or SSL using a client
certificate. In your project you can use the `options` parameter to specify the
connection options for the server.
Example using a custom repository using SSH (requires the SSH2 PECL extension):
{
"repositories": [
{
"type": "composer",
"url": "ssh2.sftp://example.org",
"options": {
"ssh2": {
"username": "composer",
"pubkey_file": "/home/composer/.ssh/id_rsa.pub",
"privkey_file": "/home/composer/.ssh/id_rsa"
}
}
}
]
}
Example using HTTP over SSL using a client certificate:
{
"repositories": [
{
"type": "composer",
"url": "https://example.org",
"options": {
"ssl": {
"cert_file": "/home/composer/.ssl/composer.pem",
}
}
}
]
}

View File

@ -27,6 +27,7 @@ use Composer\Util\RemoteFilesystem;
class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface, StreamableRepositoryInterface class ComposerRepository extends ArrayRepository implements NotifiableRepositoryInterface, StreamableRepositoryInterface
{ {
protected $config; protected $config;
protected $options;
protected $url; protected $url;
protected $io; protected $io;
protected $cache; protected $cache;
@ -37,7 +38,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
public function __construct(array $repoConfig, IOInterface $io, Config $config) public function __construct(array $repoConfig, IOInterface $io, Config $config)
{ {
if (!preg_match('{^\w+://}', $repoConfig['url'])) { if (!preg_match('{^[\w.]+://}', $repoConfig['url'])) {
// assume http as the default protocol // assume http as the default protocol
$repoConfig['url'] = 'http://'.$repoConfig['url']; $repoConfig['url'] = 'http://'.$repoConfig['url'];
} }
@ -46,7 +47,12 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']); throw new \UnexpectedValueException('Invalid url given for Composer repository: '.$repoConfig['url']);
} }
if (!isset($repoConfig['options'])) {
$repoConfig['options'] = array();
}
$this->config = $config; $this->config = $config;
$this->options = $repoConfig['options'];
$this->url = $repoConfig['url']; $this->url = $repoConfig['url'];
$this->io = $io; $this->io = $io;
$this->cache = new Cache($io, $config->get('home').'/cache/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url)); $this->cache = new Cache($io, $config->get('home').'/cache/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url));
@ -199,7 +205,7 @@ class ComposerRepository extends ArrayRepository implements NotifiableRepository
$jsonUrl = $this->url . '/packages.json'; $jsonUrl = $this->url . '/packages.json';
} }
$json = new JsonFile($jsonUrl, new RemoteFilesystem($this->io)); $json = new JsonFile($jsonUrl, new RemoteFilesystem($this->io, $this->options));
$data = $json->read(); $data = $json->read();
if (!empty($data['notify'])) { if (!empty($data['notify'])) {

View File

@ -30,15 +30,17 @@ class RemoteFilesystem
private $result; private $result;
private $progress; private $progress;
private $lastProgress; private $lastProgress;
private $options;
/** /**
* Constructor. * Constructor.
* *
* @param IOInterface $io The IO instance * @param IOInterface $io The IO instance
*/ */
public function __construct(IOInterface $io) public function __construct(IOInterface $io, $options = array())
{ {
$this->io = $io; $this->io = $io;
$this->options = $options;
} }
/** /**
@ -241,6 +243,8 @@ class RemoteFilesystem
$options['http']['header'] .= "Authorization: Basic $authStr\r\n"; $options['http']['header'] .= "Authorization: Basic $authStr\r\n";
} }
$options = array_replace_recursive($options, $this->options);
return $options; return $options;
} }
} }

View File

@ -47,6 +47,23 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
$this->assertContains('Authorization: Basic', $options['http']['header']); $this->assertContains('Authorization: Basic', $options['http']['header']);
} }
public function testGetOptionsForUrlWithStreamOptions()
{
$io = $this->getMock('Composer\IO\IOInterface');
$io
->expects($this->once())
->method('hasAuthorization')
->will($this->returnValue(true))
;
$streamOptions = array('ssl' => array(
'allow_self_signed' => true,
));
$res = $this->callGetOptionsForUrl($io, array('https://example.org'), $streamOptions);
$this->assertTrue(isset($res['ssl']) && isset($res['ssl']['allow_self_signed']) && true === $res['ssl']['allow_self_signed'], 'getOptions must return an array with a allow_self_signed set to true');
}
public function testCallbackGetFileSize() public function testCallbackGetFileSize()
{ {
$fs = new RemoteFilesystem($this->getMock('Composer\IO\IOInterface')); $fs = new RemoteFilesystem($this->getMock('Composer\IO\IOInterface'));
@ -102,9 +119,9 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
unlink($file); unlink($file);
} }
protected function callGetOptionsForUrl($io, array $args = array()) protected function callGetOptionsForUrl($io, array $args = array(), array $options = array())
{ {
$fs = new RemoteFilesystem($io); $fs = new RemoteFilesystem($io, $options);
$ref = new \ReflectionMethod($fs, 'getOptionsForUrl'); $ref = new \ReflectionMethod($fs, 'getOptionsForUrl');
$ref->setAccessible(true); $ref->setAccessible(true);