Skip to content

Commit

Permalink
Add authentication
Browse files Browse the repository at this point in the history
Move authentication behavior to its proper place
  • Loading branch information
sagikazarmark committed Dec 21, 2015
1 parent ab4224f commit 4f60a0e
Show file tree
Hide file tree
Showing 14 changed files with 741 additions and 0 deletions.
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"Http\\Message\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"spec\\Http\\Message\\": "spec/"
}
},
"scripts": {
"test": "vendor/bin/phpspec run",
"test-ci": "vendor/bin/phpspec run -c phpspec.yml.ci"
Expand Down
11 changes: 11 additions & 0 deletions spec/Authentication/AuthenticationBehavior.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace spec\Http\Message\Authentication;

trait AuthenticationBehavior
{
function it_is_an_authentication()
{
$this->shouldImplement('Http\Message\Authentication');
}
}
52 changes: 52 additions & 0 deletions spec/Authentication/BasicAuthSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace spec\Http\Message\Authentication;

use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;

class BasicAuthSpec extends ObjectBehavior
{
use AuthenticationBehavior;

function let()
{
$this->beConstructedWith('john.doe', 'secret');
}

function it_is_initializable()
{
$this->shouldHaveType('Http\Message\Authentication\BasicAuth');
}

function it_has_a_username()
{
$this->getUsername()->shouldReturn('john.doe');
}

function it_accepts_a_username()
{
$this->setUsername('jane.doe');

$this->getUsername()->shouldReturn('jane.doe');
}

function it_has_a_password()
{
$this->getPassword()->shouldReturn('secret');
}

function it_accepts_a_password()
{
$this->setPassword('very_secret');

$this->getPassword()->shouldReturn('very_secret');
}

function it_authenticates_a_request(RequestInterface $request, RequestInterface $newRequest)
{
$request->withHeader('Authorization', 'Basic '.base64_encode('john.doe:secret'))->willReturn($newRequest);

$this->authenticate($request)->shouldReturn($newRequest);
}
}
40 changes: 40 additions & 0 deletions spec/Authentication/BearerSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace spec\Http\Message\Authentication;

use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;

class BearerSpec extends ObjectBehavior
{
use AuthenticationBehavior;

function let()
{
$this->beConstructedWith('token');
}

function it_is_initializable()
{
$this->shouldHaveType('Http\Message\Authentication\Bearer');
}

function it_has_a_token()
{
$this->getToken()->shouldReturn('token');
}

function it_accepts_a_token()
{
$this->setToken('another_token');

$this->getToken()->shouldReturn('another_token');
}

function it_authenticates_a_request(RequestInterface $request, RequestInterface $newRequest)
{
$request->withHeader('Authorization', 'Bearer token')->willReturn($newRequest);

$this->authenticate($request)->shouldReturn($newRequest);
}
}
81 changes: 81 additions & 0 deletions spec/Authentication/ChainSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace spec\Http\Message\Authentication;

use Http\Message\Authentication;
use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;

class ChainSpec extends ObjectBehavior
{
use AuthenticationBehavior;

function it_is_initializable()
{
$this->shouldHaveType('Http\Message\Authentication\Chain');
}

function it_accepts_an_authentication_chain_in_the_constructor(Authentication $auth1, Authentication $auth2)
{
$chain = [$auth1, $auth2];

$this->beConstructedWith($chain);

$this->getAuthenticationChain()->shouldReturn($chain);
}

function it_sets_the_authentication_chain(Authentication $auth1, Authentication $auth2)
{
// This SHOULD be replaced
$this->beConstructedWith([$auth1]);

$this->setAuthenticationChain([$auth2]);

$this->getAuthenticationChain()->shouldReturn([$auth2]);
}

function it_adds_an_authentication_method(Authentication $auth1, Authentication $auth2)
{
// This SHOULD NOT be replaced
$this->beConstructedWith([$auth1]);

$this->addAuthentication($auth2);

$this->getAuthenticationChain()->shouldReturn([$auth1, $auth2]);
}

function it_clears_the_authentication_chain(Authentication $auth1, Authentication $auth2)
{
// This SHOULD be replaced
$this->beConstructedWith([$auth1]);

$this->clearAuthenticationChain();

$this->addAuthentication($auth2);

$this->getAuthenticationChain()->shouldReturn([$auth2]);
}

function it_authenticates_a_request(
Authentication $auth1,
Authentication $auth2,
RequestInterface $originalRequest,
RequestInterface $request1,
RequestInterface $request2
) {
$originalRequest->withHeader('AuthMethod1', 'AuthValue')->willReturn($request1);
$request1->withHeader('AuthMethod2', 'AuthValue')->willReturn($request2);

$auth1->authenticate($originalRequest)->will(function ($args) {
return $args[0]->withHeader('AuthMethod1', 'AuthValue');
});

$auth2->authenticate($request1)->will(function ($args) {
return $args[0]->withHeader('AuthMethod2', 'AuthValue');
});

$this->beConstructedWith([$auth1, $auth2]);

$this->authenticate($originalRequest)->shouldReturn($request2);
}
}
75 changes: 75 additions & 0 deletions spec/Authentication/MatchingSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace spec\Http\Message\Authentication;

use Http\Message\Authentication;
use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;

class MatchingSpec extends ObjectBehavior
{
use AuthenticationBehavior;

private $matcher;

function let(Authentication $authentication)
{
$this->matcher = function($request) { return true; };

$this->beConstructedWith($authentication, $this->matcher);
}

function it_is_initializable()
{
$this->shouldHaveType('Http\Message\Authentication\Matching');
}

function it_has_an_authentication(Authentication $authentication)
{
$this->getAuthentication()->shouldReturn($authentication);
}

function it_accepts_an_authentication(Authentication $anotherAuthentication)
{
$this->setAuthentication($anotherAuthentication);

$this->getAuthentication()->shouldReturn($anotherAuthentication);
}

function it_has_a_matcher()
{
$this->getMatcher()->shouldReturn($this->matcher);
}

function it_accepts_a_matcher()
{
$matcher = function($request) { return false; };

$this->setMatcher($matcher);

$this->getMatcher()->shouldReturn($matcher);
}

function it_authenticates_a_request(Authentication $authentication, RequestInterface $request, RequestInterface $newRequest)
{
$authentication->authenticate($request)->willReturn($newRequest);

$this->authenticate($request)->shouldReturn($newRequest);
}

function it_does_not_authenticate_a_request(Authentication $authentication, RequestInterface $request)
{
$matcher = function($request) { return false; };

$this->setMatcher($matcher);

$authentication->authenticate($request)->shouldNotBeCalled();

$this->authenticate($request)->shouldReturn($request);
}

function it_creates_a_matcher_from_url(Authentication $authentication)
{
$this->createUrlMatcher($authentication, 'url')->shouldHaveType('Http\Message\Authentication\Matching');
}
}
59 changes: 59 additions & 0 deletions spec/Authentication/WsseSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace spec\Http\Message\Authentication;

use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class WsseSpec extends ObjectBehavior
{
use AuthenticationBehavior;

function let()
{
$this->beConstructedWith('john.doe', 'secret');
}

function it_is_initializable()
{
$this->shouldHaveType('Http\Message\Authentication\Wsse');
}

function it_has_a_username()
{
$this->getUsername()->shouldReturn('john.doe');
}

function it_accepts_a_username()
{
$this->setUsername('jane.doe');

$this->getUsername()->shouldReturn('jane.doe');
}

function it_has_a_password()
{
$this->getPassword()->shouldReturn('secret');
}

function it_accepts_a_password()
{
$this->setPassword('very_secret');

$this->getPassword()->shouldReturn('very_secret');
}

function it_authenticates_a_request(
RequestInterface $request,
RequestInterface $newRequest,
RequestInterface $newerRequest
) {
$request->withHeader('Authorization', 'WSSE profile="UsernameToken"')->willReturn($newRequest);
$newRequest->withHeader('X-WSSE', Argument::that(function($arg) {
return preg_match('/UsernameToken Username=".*", PasswordDigest=".*", Nonce=".*", Created=".*"/', $arg);
}))->willReturn($newerRequest);

$this->authenticate($request)->shouldReturn($newerRequest);
}
}
22 changes: 22 additions & 0 deletions src/Authentication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Http\Message;

use Psr\Http\Message\RequestInterface;

/**
* Authenticate a PSR-7 Request.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
interface Authentication
{
/**
* Authenticates a request.
*
* @param RequestInterface $request
*
* @return RequestInterface
*/
public function authenticate(RequestInterface $request);
}
36 changes: 36 additions & 0 deletions src/Authentication/BasicAuth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Http\Message\Authentication;

use Http\Message\Authentication;
use Psr\Http\Message\RequestInterface;

/**
* Authenticate a PSR-7 Request using Basic Auth.
*
* @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
*/
final class BasicAuth implements Authentication
{
use UserPasswordPair;

/**
* @param string $username
* @param string $password
*/
public function __construct($username, $password)
{
$this->username = $username;
$this->password = $password;
}

/**
* {@inheritdoc}
*/
public function authenticate(RequestInterface $request)
{
$header = sprintf('Basic %s', base64_encode(sprintf('%s:%s', $this->username, $this->password)));

return $request->withHeader('Authorization', $header);
}
}

0 comments on commit 4f60a0e

Please sign in to comment.