From 6359d73aa1bdba40bdc181d669611f31465c7fee Mon Sep 17 00:00:00 2001 From: Ramon Kleiss Date: Sat, 13 Apr 2013 02:18:00 +0200 Subject: [PATCH] Added support for authentication --- README.md | 17 +++++- lib/Transmission/Client.php | 38 +++++++++++- .../Exception/AuthenticationException.php | 9 +++ tests/Transmission/Tests/ClientTest.php | 59 +++++++++++++++++++ 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 lib/Transmission/Exception/AuthenticationException.php diff --git a/README.md b/README.md index ea9411d..5e13b39 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ $files = $torrent->getFiles(); ``` To find out which information is contained by the torrent, check -[Transmission\Torrent](https://github.com/kleiram/transmission-php/tree/master/lib/Transmission/Torrent.php). +[Transmission\Model\Torrent](https://github.com/kleiram/transmission-php/tree/master/lib/Transmission/Model/Torrent.php). By default, the library will try to connect to `localhost:9091`. If you want to connect to an other host or port you can create a new `Transmission\Client` and @@ -84,6 +84,21 @@ use Transmission\Torrent; $torrent = Torrent::add(/* base64-encoded metainfo */, null, true); ``` +If the Transmission server is secured with a username and password you can +authenticate using the `Client` class: + +```php +authenticate('username', 'password'); + +// And you should pass the client to any static method you call! +$torrent = Torrent::get(1, $client); +``` + For more examples, see the [`examples`](https://github.com/kleiram/transmission-php/tree/master/examples) directory. diff --git a/lib/Transmission/Client.php b/lib/Transmission/Client.php index 37543c7..8603d68 100644 --- a/lib/Transmission/Client.php +++ b/lib/Transmission/Client.php @@ -2,7 +2,9 @@ namespace Transmission; use Buzz\Browser; +use Buzz\Listener\BasicAuthListener; use Transmission\Exception\ConnectionException; +use Transmission\Exception\AuthenticationException; use Transmission\Exception\InvalidResponseException; use Transmission\Exception\UnexpectedResponseException; @@ -43,6 +45,11 @@ class Client */ protected $browser; + /** + * @var string + */ + protected $authDigest; + /** * Constructor * @@ -56,6 +63,17 @@ public function __construct($host = null, $port = null) $this->setBrowser(new Browser()); } + /** + * Authenticate against the Transmission server + * + * @param string $username + * @param string $password + */ + public function authenticate($username, $password) + { + $this->authDigest = base64_encode($username .':'. $password); + } + /** * Make an API call * @@ -77,12 +95,20 @@ public function call($method, array $arguments = array(), $tag = null) $content['tag'] = (string) $tag; } + $headers = array(); + + if (is_string($this->getToken())) { + $headers[] = sprintf('%s: %s', self::TOKEN_HEADER, $this->getToken()); + } + + if (is_string($this->authDigest)) { + $headers[] = sprintf('Authorization: Basic %s', $this->authDigest); + } + try { $response = $this->browser->post( $this->getUrl(), - array(sprintf( - '%s: %s', self::TOKEN_HEADER, $this->getToken() - )), + $headers, json_encode($content) ); } catch (\Exception $e) { @@ -105,6 +131,12 @@ public function call($method, array $arguments = array(), $tag = null) return $this->call($method, $arguments, $tag); } + if ($statusCode === 401) { + throw new AuthenticationException( + 'Access to the Transmission server requires authentication' + ); + } + if ($statusCode !== 200) { throw new UnexpectedResponseException( 'Unexpected response received from Transmission' diff --git a/lib/Transmission/Exception/AuthenticationException.php b/lib/Transmission/Exception/AuthenticationException.php new file mode 100644 index 0000000..017696a --- /dev/null +++ b/lib/Transmission/Exception/AuthenticationException.php @@ -0,0 +1,9 @@ + + */ +class AuthenticationException extends \RuntimeException +{ +} diff --git a/tests/Transmission/Tests/ClientTest.php b/tests/Transmission/Tests/ClientTest.php index 76af411..8e63115 100644 --- a/tests/Transmission/Tests/ClientTest.php +++ b/tests/Transmission/Tests/ClientTest.php @@ -67,6 +67,65 @@ public function shouldMakeApiCalls() $this->assertEquals('bar', $stdClass->foo); } + /** + * @test + */ + public function shouldAuthenticate() + { + $response = $this->getMock('Buzz\Message\Response'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->will($this->returnValue(200)); + + $response + ->expects($this->once()) + ->method('getContent') + ->will($this->returnValue('{"foo":"bar"}')); + + $browser = $this->getMock('Buzz\Browser'); + $browser + ->expects($this->once()) + ->method('post') + ->with( + 'http://localhost:9091/transmission/rpc', + array( + 'X-Transmission-Session-Id: foo', + 'Authorization: Basic '. base64_encode('foo:bar') + ) + ) + ->will($this->returnValue($response)); + + $client = new Client(); + $client->setToken('foo'); + $client->setBrowser($browser); + $client->authenticate('foo', 'bar'); + $client->call('foo'); + } + + /** + * @test + * @expectedException Transmission\Exception\AuthenticationException + */ + public function shouldThrowExceptionOnAuthenticationError() + { + $response = $this->getMock('Buzz\Message\Response'); + $response + ->expects($this->once()) + ->method('getStatusCode') + ->will($this->returnValue(401)); + + $browser = $this->getMock('Buzz\Browser'); + $browser + ->expects($this->once()) + ->method('post') + ->will($this->returnValue($response)); + + $client = new Client(); + $client->setBrowser($browser); + $client->call('foo'); + } + /** * @test */