Skip to content

Commit

Permalink
Adding support for connection authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
mariano committed May 18, 2015
1 parent 8f8cb5a commit 3a9d7ff
Show file tree
Hide file tree
Showing 13 changed files with 307 additions and 26 deletions.
11 changes: 9 additions & 2 deletions CHANGELOG.md
Expand Up @@ -3,11 +3,17 @@
All Notable changes will be documented in this file. This project adheres to
[Semantic Versioning](http://semver.org/).

## [1.2.2]
## [1.3.0] - 2015-05-18

### Added
- Added support for `WORKING`.
- Added `processing()` method to Queue API.
- Added `$password` option to `addServer()` in `Disque\Client`.

### Changed
- By default when creating a new `Disque\Client` without arguments NO server
is pre-loaded. You will have to manually add servers via `addServer()`, or
specify them to the `Disque\Client` constructor.

## [1.2.1] - 2015-05-14

Expand Down Expand Up @@ -94,7 +100,8 @@ parameters were specified, an `InvalidCommandArgumentException` was thrown.
- Added support for Predis connections, and allowing adding new connection
methods via `ConnectionInterface`.

[1.2.2]: https://github.com/mariano/disque-php/compare/1.2.1...HEAD
[1.3.1]: https://github.com/mariano/disque-php/compare/1.3.1...HEAD
[1.3.0]: https://github.com/mariano/disque-php/compare/tag/1.3.0
[1.2.1]: https://github.com/mariano/disque-php/releases/tag/1.2.1
[1.2.0]: https://github.com/mariano/disque-php/releases/tag/1.2.0
[1.1.0]: https://github.com/mariano/disque-php/releases/tag/1.1.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -103,7 +103,7 @@ instead of using the issue tracker.
- [x] Higher level API for queueing and retrieving jobs
- [x] Method in `Queue` to schedule future jobs based on DateTime
- [x] `QSCAN`, `WORKING`
- [ ] Add support for AUTH based connections
- [x] Add support for AUTH based connections
- [x] Add support to `WORKING` command on Queue API
- [ ] `QSTAT` when they are implemented upstream

Expand Down
16 changes: 14 additions & 2 deletions docs/README.md
Expand Up @@ -20,11 +20,12 @@ $client = new \Disque\Client([
]);
```

If no host is specified, `127.0.0.1:7711` is assumed. Hosts can also be added
via the `addServer($host, $port)` method:
If no host is specified, no server is initialized. Hosts can also be added
via the `addServer($host, $port, $password)` method:

```php
$client = new \Disque\Client();
$client->addServer('127.0.0.1', 7711, 'my_password');
$client->addServer('127.0.0.1', 7712);
```

Expand Down Expand Up @@ -341,6 +342,17 @@ $result = $client->connect();
var_dump($result);
```

You can also specify servers that require a password for connection. To do so,
use the `addServer()` method, like so:

```php
$client = new \Disque\Client();
$client->addServer('127.0.0.1', 7711, 'my_password');
$client->addServer('127.0.0.1, 7712, 'my_password2');
$result = $client->connect();
var_dump($result);
```

The above `connect()` call will return an output similar to the following:

```php
Expand Down
7 changes: 4 additions & 3 deletions src/Client.php
Expand Up @@ -59,7 +59,7 @@ class Client
*
* @param array $servers Servers (`host`:`port`)
*/
public function __construct(array $servers = ['127.0.0.1:7711'])
public function __construct(array $servers = [])
{
foreach ([
'ACKJOB' => Command\AckJob::class,
Expand Down Expand Up @@ -115,12 +115,13 @@ public function getConnectionManager()
*
* @param string $host Host
* @param int $port Port
* @param string $password Password to use when connecting to this server
* @return void
* @throws InvalidArgumentException
*/
public function addServer($host, $port = 7711)
public function addServer($host, $port = 7711, $password = null)
{
$this->connectionManager->addServer($host, $port);
$this->connectionManager->addServer($host, $port, $password);
}

/**
Expand Down
24 changes: 24 additions & 0 deletions src/Command/Auth.php
@@ -0,0 +1,24 @@
<?php
namespace Disque\Command;

use Disque\Command\Response\AuthResponse;

class Auth extends BaseCommand implements CommandInterface
{
/**
* Tells the argument types for this command
*
* @var int
*/
protected $argumentsType = self::ARGUMENTS_TYPE_STRING;

/**
* Get command
*
* @return string Command
*/
public function getCommand()
{
return 'AUTH';
}
}
6 changes: 6 additions & 0 deletions src/Connection/AuthenticationException.php
@@ -0,0 +1,6 @@
<?php
namespace Disque\Connection;

class AuthenticationException extends ConnectionException
{
}
37 changes: 33 additions & 4 deletions src/Connection/Manager.php
Expand Up @@ -2,6 +2,7 @@
namespace Disque\Connection;

use InvalidArgumentException;
use Disque\Command\Auth;
use Disque\Command\CommandInterface;
use Disque\Command\GetJob;
use Disque\Command\Hello;
Expand Down Expand Up @@ -102,17 +103,18 @@ public function getServers()
*
* @param string $host Host
* @param int $port Port
* @param string $password Password to use when connecting to this server
* @return void
* @throws InvalidArgumentException
*/
public function addServer($host, $port = 7711)
public function addServer($host, $port = 7711, $password = null)
{
if (!is_string($host) || !is_int($port)) {
throw new InvalidArgumentException('Invalid server specified');
}

$port = (int) $port;
$this->servers[] = compact('host', 'port');
$this->servers[] = compact('host', 'port', 'password');
}

/**
Expand Down Expand Up @@ -155,6 +157,7 @@ public function isConnected()
* Connect to Disque
*
* @return array Connected node information
* @throws AuthenticationException
* @throws ConnectionException
*/
public function connect()
Expand Down Expand Up @@ -215,6 +218,7 @@ public function execute(CommandInterface $command)
*
* @return array Indexed array with `connection` and `hello`. `connection`
* could end up being null
* @throws AuthenticationException
* @throws ConnectionException
*/
protected function findAvailableConnection()
Expand Down Expand Up @@ -246,25 +250,50 @@ protected function findAvailableConnection()
/**
* Get a node connection and its HELLO result
*
* @param array $server Server (with `host`, and `port`)
* @param array $server Server (with `host`, `port`, and `password`)
* @return array Indexed array with `connection` and `hello`. `connection`
* could end up being null
* @throws AuthenticationException
*/
protected function getNodeConnection(array $server)
{
$helloCommand = new Hello();
$connection = $this->buildConnection($server['host'], $server['port']);
$hello = [];
try {
$connection->connect($this->options);
$this->doConnect($connection, $server, $this->options);
$hello = $helloCommand->parse($connection->execute($helloCommand));
} catch (ConnectionException $e) {
$message = $e->getMessage();
if (stripos($message, 'NOAUTH') === 0) {
throw new AuthenticationException($message);
}
$connection = null;
$hello = [];
}
return compact('connection', 'hello');
}

/**
* Actually perform the connection
*
* @param ConnectionInterface $connection Connection
* @param array $server Server (with `host`, `port`, and `password`)
* @param array $options Connection options
*/
private function doConnect(ConnectionInterface $connection, array $server, array $options)
{
$connection->connect($options);
if (!empty($server['password'])) {
$authCommand = new Auth();
$authCommand->setArguments([$server['password']]);
$response = $authCommand->parse($connection->execute($authCommand));
if ($response !== 'OK') {
throw new AuthenticationException();
}
}
}

/**
* Build a new connection
*
Expand Down
1 change: 1 addition & 0 deletions src/Connection/ManagerInterface.php
Expand Up @@ -65,6 +65,7 @@ public function isConnected();
* Connect to Disque
*
* @return array Connected node information
* @throws AuthenticationException
* @throws ConnectionException
*/
public function connect();
Expand Down
2 changes: 1 addition & 1 deletion src/Connection/Socket.php
Expand Up @@ -149,7 +149,7 @@ public function receive($keepWaiting = false)
return (string) $data;
case '-':
$data = (string) $data;
throw new ResponseException("Error received from client: {$data}");
throw new ResponseException($data);
case ':':
return (int) $data;
case '$':
Expand Down
12 changes: 5 additions & 7 deletions tests/ClientTest.php
Expand Up @@ -49,9 +49,7 @@ public function testInstance()
public function testConstruct()
{
$c = new MockClient();
$this->assertSame([
['host' => '127.0.0.1', 'port' => 7711]
], $c->getConnectionManager()->getServers());
$this->assertSame([], $c->getConnectionManager()->getServers());
}

public function testConstructNoServers()
Expand All @@ -73,8 +71,8 @@ public function testConstructMultipleServers()
'127.0.0.1:7712',
]);
$this->assertSame([
['host' => '127.0.0.1', 'port' => 7711],
['host' => '127.0.0.1', 'port' => 7712],
['host' => '127.0.0.1', 'port' => 7711, 'password' => null],
['host' => '127.0.0.1', 'port' => 7712, 'password' => null],
], $c->getConnectionManager()->getServers());
}

Expand All @@ -85,8 +83,8 @@ public function testConstructMultipleServersDefaultPort()
'127.0.0.1:7712',
]);
$this->assertSame([
['host' => '127.0.0.1', 'port' => 7711],
['host' => '127.0.0.1', 'port' => 7712],
['host' => '127.0.0.1', 'port' => 7711, 'password' => null],
['host' => '127.0.0.1', 'port' => 7712, 'password' => null],
], $c->getConnectionManager()->getServers());
}

Expand Down
95 changes: 95 additions & 0 deletions tests/Command/AuthTest.php
@@ -0,0 +1,95 @@
<?php
namespace Disque\Test\Command;

use PHPUnit_Framework_TestCase;
use Disque\Command\Argument\InvalidCommandArgumentException;
use Disque\Command\CommandInterface;
use Disque\Command\Auth;
use Disque\Command\Response\InvalidResponseException;

class AuthTest extends PHPUnit_Framework_TestCase
{
public function testInstance()
{
$c = new Auth();
$this->assertInstanceOf(CommandInterface::class, $c);
}

public function testGetCommand()
{
$c = new Auth();
$result = $c->getCommand();
$this->assertSame('AUTH', $result);
}

public function testIsBlocking()
{
$c = new Auth();
$result = $c->isBlocking();
$this->assertFalse($result);
}

public function testBuildInvalidArgumentsEmpty()
{
$this->setExpectedException(InvalidCommandArgumentException::class, 'Invalid command arguments. Arguments for command Disque\\Command\\Auth: []');
$c = new Auth();
$c->setArguments([]);
}

public function testBuildInvalidArgumentsEmptyTooMany()
{
$this->setExpectedException(InvalidCommandArgumentException::class, 'Invalid command arguments. Arguments for command Disque\\Command\\Auth: ["test","stuff"]');
$c = new Auth();
$c->setArguments(['test', 'stuff']);
}

public function testBuildInvalidArgumentsEmptyNonNumeric()
{
$this->setExpectedException(InvalidCommandArgumentException::class, 'Invalid command arguments. Arguments for command Disque\\Command\\Auth: {"test":"stuff"}');
$c = new Auth();
$c->setArguments(['test' => 'stuff']);
}

public function testBuildInvalidArgumentsNumericNon0()
{
$this->setExpectedException(InvalidCommandArgumentException::class, 'Invalid command arguments. Arguments for command Disque\\Command\\Auth: {"1":"stuff"}');
$c = new Auth();
$c->setArguments([1 => 'stuff']);
}

public function testBuildInvalidArgumentsNonString()
{
$this->setExpectedException(InvalidCommandArgumentException::class, 'Invalid command arguments. Arguments for command Disque\\Command\\Auth: [false]');
$c = new Auth();
$c->setArguments([false]);
}

public function testBuild()
{
$c = new Auth();
$c->setArguments(['test']);
$result = $c->getArguments();
$this->assertSame(['test'], $result);
}

public function testParseInvalidNonString()
{
$this->setExpectedException(InvalidResponseException::class, 'Invalid command response. Command Disque\\Command\\Auth got: 10');
$c = new Auth();
$c->parse(10);
}

public function testParseInvalidNonStringBoolTrue()
{
$this->setExpectedException(InvalidResponseException::class, 'Invalid command response. Command Disque\\Command\\Auth got: true');
$c = new Auth();
$c->parse(true);
}

public function testParse()
{
$c = new Auth();
$result = $c->parse('OK');
$this->assertSame('OK', $result);
}
}

0 comments on commit 3a9d7ff

Please sign in to comment.