-
-
Notifications
You must be signed in to change notification settings - Fork 140
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from GeniusesOfSymfony/refactoring-client-auth
Refacto client auth
- Loading branch information
Showing
15 changed files
with
466 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
Oops, something went wrong.