Skip to content

Commit

Permalink
Merge pull request #28 from GeniusesOfSymfony/refactoring-client-auth
Browse files Browse the repository at this point in the history
Refacto client auth
  • Loading branch information
Johann Saunier committed Jul 4, 2015
2 parents 542adc4 + 31f6050 commit 45b2c4f
Show file tree
Hide file tree
Showing 15 changed files with 466 additions and 163 deletions.
134 changes: 134 additions & 0 deletions Client/Auth/WebsocketAuthenticationProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<?php

namespace Gos\Bundle\WebSocketBundle\Client\Auth;

use Gos\Bundle\WebSocketBundle\Client\ClientStorageInterface;
use Gos\Bundle\WebSocketBundle\Client\Exception\StorageException;
use Psr\Log\LoggerInterface;
use Ratchet\ConnectionInterface;
use Symfony\Component\HttpKernel\Log\NullLogger;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\User\UserInterface;

class WebsocketAuthenticationProvider implements WebsocketAuthenticationProviderInterface
{
/**
* @var SecurityContextInterface
*/
protected $securityContext;

/**
* @var array
*/
protected $firewalls;

/**
* @var LoggerInterface
*/
protected $logger;

/**
* @var ClientStorageInterface
*/
protected $clientStorage;

/**
* @param SecurityContextInterface $securityContext
* @param array $firewalls
* @param ClientStorageInterface $clientStorage
* @param LoggerInterface $logger
*/
public function __construct(
SecurityContextInterface $securityContext,
$firewalls = array(),
ClientStorageInterface $clientStorage,
LoggerInterface $logger = null
) {
$this->securityContext = $securityContext;
$this->firewalls = $firewalls;
$this->clientStorage = $clientStorage;
$this->logger = null === $logger ? new NullLogger() : $logger;
}

protected function getToken(ConnectionInterface $connection)
{
$token = null;

if (isset($connection->Session) && $connection->Session) {
foreach ($this->firewalls as $firewall) {
if (false !== $serializedToken = $connection->Session->get('_security_' . $firewall, false)) {
/** @var TokenInterface $token */
$token = unserialize($serializedToken);
break;
}
}
}

if (null === $token) {
$token = new AnonymousToken($this->firewalls[0], 'anon-' . $connection->WAMP->sessionId);
}

if ($this->securityContext->getToken() !== $token) {
$this->securityContext->setToken($token);
}

return $token;
}

/**
* @param TokenInterface $token
*/
public function authenticate(ConnectionInterface $conn)
{
if (1 === count($this->firewalls) && 'ws_firewall' === $this->firewalls[0]) {
$this->logger->warning(sprintf(
'User firewall is not configured, we have set %s by default',
$this->firewalls[0])
);
}

$loggerContext = array(
'connection_id' => $conn->resourceId,
'session_id' => $conn->WAMP->sessionId,
);

$token = $this->getToken($conn);
$user = $token->getUser();
$username = $user instanceof UserInterface ? $user->getUsername() : $user;

try {
$identifier = $this->clientStorage->getStorageId($conn, $username);
} catch (StorageException $e) {
$this->logger->error(
$e->getMessage(),
$loggerContext
);

throw $e;
}

$loggerContext['storage_id'] = $identifier;
$this->clientStorage->addClient($identifier, $token->getUser());
$conn->WAMP->clientStorageId = $identifier;

$this->logger->info(sprintf(
'%s connected [%]',
$username,
$user instanceof UserInterface ? implode(', ', $user->getRoles()) : array()
), $loggerContext);

return $token;
}

/**
* @param TokenInterface $token
*
* @return bool
*/
public function supports(TokenInterface $token)
{
return $token instanceof WebsocketToken;
}
}
17 changes: 17 additions & 0 deletions Client/Auth/WebsocketAuthenticationProviderInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Gos\Bundle\WebSocketBundle\Client\Auth;


use Ratchet\ConnectionInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;

interface WebsocketAuthenticationProviderInterface
{
/**
* @param ConnectionInterface $conn
*
* @return TokenInterface
*/
public function authenticate(ConnectionInterface $conn);
}
131 changes: 131 additions & 0 deletions Client/ClientManipulator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

namespace Gos\Bundle\WebSocketBundle\Client;

use Gos\Bundle\WebSocketBundle\Client\Auth\WebsocketAuthenticationProviderInterface;
use Gos\Bundle\WebSocketBundle\Client\Exception\ClientNotFoundException;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\User\UserInterface;

class ClientManipulator implements ClientManipulatorInterface
{
/**
* @var ClientStorageInterface
*/
protected $clientStorage;

/**
* @var WebsocketAuthenticationProviderInterface
*/
protected $authenticationProvider;

/**
* @param ClientStorageInterface $clientStorage
* @param WebsocketAuthenticationProviderInterface $authenticationProvider
*/
public function __construct(ClientStorageInterface $clientStorage, WebsocketAuthenticationProviderInterface $authenticationProvider)
{
$this->clientStorage = $clientStorage;
$this->authenticationProvider = $authenticationProvider;
}

/**
* @param ConnectionInterface $connection
*
* @return false|string|\Symfony\Component\Security\Core\User\UserInterface
*/
public function getClient(ConnectionInterface $connection)
{
$storageId = $this->clientStorage->getStorageId($connection);

try{
return $this->clientStorage->getClient($storageId);
} catch (ClientNotFoundException $e) { //User is gone due to ttl
$this->authenticationProvider->authenticate($connection);
return $this->getClient($connection);
}
}

/**
* @param Topic $topic
* @param string $username
*
* @return false|string|\Symfony\Component\Security\Core\User\UserInterface
*/
public function findByUsername(Topic $topic, $username)
{
foreach($topic as $connection){
$client = $this->getClient($connection);

if($client instanceof AnonymousToken || false === $client){
continue;
}

if($client->getUsername() === $username){
return ['client' => $client, 'connection' => $connection];
}
}

return false;
}

/**
* @param Topic $topic
* @param bool $anonymous
*
* @return false|string|UserInterface
*/
public function getAll(Topic $topic, $anonymous = false)
{
$results = [];

foreach($topic as $connection){
$client = $this->getClient($connection);

if(true !== $anonymous && ($client instanceof AnonymousToken || false === $client)){
continue;
}

$results[] = [
'client' => $client,
'connection' => $connection
];
}

return empty($results) ? false : $results;
}

/**
* @param Topic $topic
* @param array $roles
*
* @return UserInterface[]
*/
public function findByRoles(Topic $topic, array $roles)
{
$results = [];

foreach($topic as $connection){
$client = $this->getClient($connection);

if($client instanceof AnonymousToken || false === $client){
continue;
}

foreach($client->getRoles() as $role){
if(in_array($role, $roles)){
$results[] = [
'client' => $client,
'connection' => $connection
];

continue 1;
}
}
}

return empty($results) ? false : $results;
}
}
41 changes: 41 additions & 0 deletions Client/ClientManipulatorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Gos\Bundle\WebSocketBundle\Client;

use Ratchet\ConnectionInterface;
use Ratchet\Wamp\Topic;
use Symfony\Component\Security\Core\User\UserInterface;

interface ClientManipulatorInterface
{
/**
* @param ConnectionInterface $connection
*
* @return false|string|\Symfony\Component\Security\Core\User\UserInterface
*/
public function getClient(ConnectionInterface $connection);

/**
* @param Topic $topic
* @param string $username
*
* @return array|false
*/
public function findByUsername(Topic $topic, $username);

/**
* @param Topic $topic
* @param array $roles
*
* @return array|false
*/
public function findByRoles(Topic $topic, array $roles);

/**
* @param Topic $topic
* @param bool $anonymous
*
* @return array|false
*/
public function getAll(Topic $topic, $anonymous = false);
}
Loading

0 comments on commit 45b2c4f

Please sign in to comment.