From 4516d0543026bedb50aa0fc631e10baa2fa48ef2 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 13:53:48 +0200 Subject: [PATCH 01/16] Add session Entity. --- Entity/Session.php | 71 ++++++++++++++++++++++++++++++++++++++++++++++ Entity/User.php | 28 ++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 Entity/Session.php diff --git a/Entity/Session.php b/Entity/Session.php new file mode 100644 index 0000000..fb26276 --- /dev/null +++ b/Entity/Session.php @@ -0,0 +1,71 @@ + + * @license MIT + * + */ +namespace phpBB\SessionsAuthBundle\Entity; + +use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Security\Core\Role\Role; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Class Session + * @package phpbb\SessionsAuthBundle\Entity + * @ORM\Entity(readOnly=true) + */ +class Session +{ + /** + * @var string + * @ORM\Column(type="string", name="session_id") + * @ORM\Id + */ + private $id; + + /** + * @var User + * @ORM\ManyToOne(targetEntity="User", inversedBy="sessions") + * @ORM\JoinColumn(name="user_id", referencedColumnName="user_id") + */ + private $user; + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @param string $id + */ + public function setId($id) + { + $this->id = $id; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @param User $user + */ + public function setUser($user) + { + $this->user = $user; + } + + + +} + diff --git a/Entity/User.php b/Entity/User.php index 5a1fd57..c5ae172 100644 --- a/Entity/User.php +++ b/Entity/User.php @@ -8,6 +8,7 @@ */ namespace phpBB\SessionsAuthBundle\Entity; +use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\User\UserInterface; @@ -49,6 +50,17 @@ class User implements UserInterface */ private $roles; + /** + * @var ArrayCollection + * @ORM\OneToMany(targetEntity="Session", mappedBy="user") + */ + private $sessions; + + public function __construct() + { + $this->sessions = new ArrayCollection(); + } + /** * Returns the roles granted to the user. * @@ -152,6 +164,22 @@ public function setEmail($email) $this->email = $email; } + /** + * @return ArrayCollection + */ + public function getSessions() + { + return $this->sessions; + } + + /** + * @param ArrayCollection $sessions + */ + public function setSessions($sessions) + { + $this->sessions = $sessions; + } + } From e25b25c1c121bff0d9764902bee6055c456ca9d0 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 14:09:34 +0200 Subject: [PATCH 02/16] Start for authentication --- Authentication/phpBBSessionAuthenticator.php | 36 +++++++++++++++++--- Resources/config/services.yml | 1 + 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index 3f315a1..c0efee0 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -9,6 +9,7 @@ namespace phpBB\SessionsAuthBundle\Authentication; +use phpBB\SessionsAuthBundle\Authentication\Provider\phpBBUserProvider; use phpBB\SessionsAuthBundle\Tokens\phpBBToken; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -17,6 +18,7 @@ use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; @@ -31,16 +33,21 @@ class phpBBSessionAuthenticator implements SimplePreAuthenticatorInterface, Auth /** @var string */ private $loginpage; + /** @var RequestStack */ + private $requestStack; + /** * @param $cookiename string * @param $boardurl string * @param $loginpage string + * @param $requestStack RequestStack */ - public function __construct($cookiename, $boardurl, $loginpage) + public function __construct($cookiename, $boardurl, $loginpage, RequestStack $requestStack) { - $this->cookiename = $cookiename; - $this->boardurl = $boardurl; - $this->loginpage = $loginpage; + $this->cookiename = $cookiename; + $this->boardurl = $boardurl; + $this->loginpage = $loginpage; + $this->requestStack = $requestStack; } @@ -48,10 +55,29 @@ public function __construct($cookiename, $boardurl, $loginpage) * @param TokenInterface $token * @param UserProviderInterface $userProvider * @param $providerKey + * @return AnonymousToken */ public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) { - // TODO: Implement authenticateToken() method. + if (!$userProvider instanceof phpBBUserProvider) + { + throw new \InvalidArgumentException( + sprintf( + 'The user provider must be an instance of phpBBUserProvider (%s was given).', + get_class($userProvider) + ) + ); + } + + $session_id = $this->requestStack->getCurrentRequest()->cookies->get($this->cookiename . '_sid'); + + if (empty($session_id)) + { + return new AnonymousToken('', $token->getUser(), $token->getRoles()); + } + + +// AnonymousToken } /** diff --git a/Resources/config/services.yml b/Resources/config/services.yml index fafc566..a15522e 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -14,6 +14,7 @@ services: - %phpbb_sessions_auth.database.cookiename% - %phpbb_sessions_auth.database.boardurl% - %phpbb_sessions_auth.database.loginpage% + - @request_stack phpbb.sessionsauthbundle.phpbb_user_provider: class: phpBB\SessionsAuthBundle\Authentication\Provider\phpBBUserProvider From db705a951f50801aebdbfd99da59609206539fe0 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 14:31:39 +0200 Subject: [PATCH 03/16] Get session from the database --- Authentication/phpBBSessionAuthenticator.php | 20 +++++++++++++++---- .../phpbbSessionsAuthExtension.php | 1 + Resources/config/services.yml | 2 ++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index c0efee0..7c513c6 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -9,8 +9,10 @@ namespace phpBB\SessionsAuthBundle\Authentication; +use Doctrine\ORM\EntityManager; use phpBB\SessionsAuthBundle\Authentication\Provider\phpBBUserProvider; use phpBB\SessionsAuthBundle\Tokens\phpBBToken; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -36,19 +38,27 @@ class phpBBSessionAuthenticator implements SimplePreAuthenticatorInterface, Auth /** @var RequestStack */ private $requestStack; + /** @var ContainerInterface */ + private $container; + + /** @var string */ + private $dbconnection; + /** * @param $cookiename string * @param $boardurl string * @param $loginpage string * @param $requestStack RequestStack + * @param ContainerInterface $container */ - public function __construct($cookiename, $boardurl, $loginpage, RequestStack $requestStack) + public function __construct($cookiename, $boardurl, $loginpage, $dbconnection, RequestStack $requestStack, ContainerInterface $container) { $this->cookiename = $cookiename; $this->boardurl = $boardurl; $this->loginpage = $loginpage; + $this->dbconnection = $dbconnection; $this->requestStack = $requestStack; - + $this->container = $container; } /** @@ -73,11 +83,13 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ if (empty($session_id)) { - return new AnonymousToken('', $token->getUser(), $token->getRoles()); + return null; // We can't authenticate if no SID is available. } + /** @var EntityManager $em */ + $em = $this->container->get('doctrine')->getManager($this->dbconnection); -// AnonymousToken + $session = $em->getRepository('phpBBSessionsAuthBundle:Session')->find($session_id); } /** diff --git a/DependencyInjection/phpbbSessionsAuthExtension.php b/DependencyInjection/phpbbSessionsAuthExtension.php index 5880773..26099e2 100644 --- a/DependencyInjection/phpbbSessionsAuthExtension.php +++ b/DependencyInjection/phpbbSessionsAuthExtension.php @@ -30,6 +30,7 @@ public function load(array $configs, ContainerBuilder $container) $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); + $container->setParameter('phpbb_sessions_auth.database.connection', $config['database']['connection']); $container->setParameter('phpbb_sessions_auth.database.prefix', $config['database']['prefix']); $container->setParameter('phpbb_sessions_auth.database.cookiename', $config['session']['cookiename']); $container->setParameter('phpbb_sessions_auth.database.boardurl', $config['session']['boardurl']); diff --git a/Resources/config/services.yml b/Resources/config/services.yml index a15522e..4037fb9 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -14,7 +14,9 @@ services: - %phpbb_sessions_auth.database.cookiename% - %phpbb_sessions_auth.database.boardurl% - %phpbb_sessions_auth.database.loginpage% + - %phpbb_sessions_auth.database.connection% - @request_stack + - @service_container phpbb.sessionsauthbundle.phpbb_user_provider: class: phpBB\SessionsAuthBundle\Authentication\Provider\phpBBUserProvider From 6861767656684651e8a6e9daf4a6ff037e6d81f9 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 14:50:55 +0200 Subject: [PATCH 04/16] Make it lowercase --- Authentication/phpBBSessionAuthenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index 7c513c6..c1927d0 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -89,7 +89,7 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ /** @var EntityManager $em */ $em = $this->container->get('doctrine')->getManager($this->dbconnection); - $session = $em->getRepository('phpBBSessionsAuthBundle:Session')->find($session_id); + $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->find($session_id); } /** From 0ab002a72aeaa970088707a22aaa4f2b15bb808c Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 14:58:44 +0200 Subject: [PATCH 05/16] Make sure Session is also converted with prefix --- Authentication/phpBBSessionAuthenticator.php | 2 +- Subscriber/TablePrefixSubscriber.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index c1927d0..e576a85 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -89,7 +89,7 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ /** @var EntityManager $em */ $em = $this->container->get('doctrine')->getManager($this->dbconnection); - $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->find($session_id); + $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($session_id); } /** diff --git a/Subscriber/TablePrefixSubscriber.php b/Subscriber/TablePrefixSubscriber.php index 89c3abc..88c6c77 100644 --- a/Subscriber/TablePrefixSubscriber.php +++ b/Subscriber/TablePrefixSubscriber.php @@ -25,11 +25,11 @@ class TablePrefixSubscriber implements EventSubscriber /** * Namespace the entity is in */ - const ENTITY_NAMESPACE = 'phpbb\\SessionAuthBundle\\Entity'; + private static $ENTITY_NAMESPACE = 'phpbb\\SessionAuthBundle\\Entity'; /** * Entity that will receive the prefix */ - const ENTITY_NAME = 'User'; + private static $ENTITY_NAME = array('User', 'Session'); /** * @var string @@ -74,7 +74,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $args) return; } - if ($classMetadata->namespace == self::ENTITY_NAMESPACE && $classMetadata->name == self::ENTITY_NAME) + if ($classMetadata->namespace == self::$ENTITY_NAMESPACE && in_array($classMetadata->name, self::$ENTITY_NAME)) { // Do not re-apply the prefix when the table is already prefixed if (false === strpos($classMetadata->getTableName(), $this->prefix)) From 6a7c99369c0a7f2617a3f876fc5eeb340070bccd Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 15:10:02 +0200 Subject: [PATCH 06/16] Fix the name matching in the prefix subscriber --- Subscriber/TablePrefixSubscriber.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Subscriber/TablePrefixSubscriber.php b/Subscriber/TablePrefixSubscriber.php index 88c6c77..12c6f2b 100644 --- a/Subscriber/TablePrefixSubscriber.php +++ b/Subscriber/TablePrefixSubscriber.php @@ -25,11 +25,11 @@ class TablePrefixSubscriber implements EventSubscriber /** * Namespace the entity is in */ - private static $ENTITY_NAMESPACE = 'phpbb\\SessionAuthBundle\\Entity'; + private static $ENTITY_NAMESPACE = 'phpBB\\SessionsAuthBundle\\Entity'; /** * Entity that will receive the prefix */ - private static $ENTITY_NAME = array('User', 'Session'); + private static $ENTITY_NAME; /** * @var string @@ -44,6 +44,7 @@ class TablePrefixSubscriber implements EventSubscriber public function __construct($prefix) { $this->prefix = (string) $prefix; + self::$ENTITY_NAME = array(self::$ENTITY_NAMESPACE . '\\User', self::$ENTITY_NAMESPACE . '\\Session'); } /** From c370a790bc20dd1eb1b4980505094e84932b320a Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 15:15:16 +0200 Subject: [PATCH 07/16] Use array() instead of [] --- Subscriber/TablePrefixSubscriber.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Subscriber/TablePrefixSubscriber.php b/Subscriber/TablePrefixSubscriber.php index 12c6f2b..fb3ca94 100644 --- a/Subscriber/TablePrefixSubscriber.php +++ b/Subscriber/TablePrefixSubscriber.php @@ -81,7 +81,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $args) if (false === strpos($classMetadata->getTableName(), $this->prefix)) { $tableName = $this->prefix . $classMetadata->getTableName(); - $classMetadata->setPrimaryTable(['name' => $tableName]); + $classMetadata->setPrimaryTable(array('name' => $tableName)); } foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) From 764f2c0d8af143db989b6beafa5759092f55f158 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 15:36:38 +0200 Subject: [PATCH 08/16] Check if user is logged in. --- Authentication/phpBBSessionAuthenticator.php | 48 ++++++++++++++------ Entity/Session.php | 2 - 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index e576a85..476e57f 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -11,6 +11,7 @@ use Doctrine\ORM\EntityManager; use phpBB\SessionsAuthBundle\Authentication\Provider\phpBBUserProvider; +use phpBB\SessionsAuthBundle\Entity\Session; use phpBB\SessionsAuthBundle\Tokens\phpBBToken; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -26,14 +27,16 @@ class phpBBSessionAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface { + const ANONYMOUS = 1; + /** @var string */ - private $cookiename; + private $cookieName; /** @var string */ - private $boardurl; + private $boardUrl; /** @var string */ - private $loginpage; + private $loginPage; /** @var RequestStack */ private $requestStack; @@ -42,7 +45,7 @@ class phpBBSessionAuthenticator implements SimplePreAuthenticatorInterface, Auth private $container; /** @var string */ - private $dbconnection; + private $dbConnection; /** * @param $cookiename string @@ -51,12 +54,13 @@ class phpBBSessionAuthenticator implements SimplePreAuthenticatorInterface, Auth * @param $requestStack RequestStack * @param ContainerInterface $container */ - public function __construct($cookiename, $boardurl, $loginpage, $dbconnection, RequestStack $requestStack, ContainerInterface $container) + public function __construct($cookiename, $boardurl, $loginpage, $dbconnection, + RequestStack $requestStack, ContainerInterface $container) { - $this->cookiename = $cookiename; - $this->boardurl = $boardurl; - $this->loginpage = $loginpage; - $this->dbconnection = $dbconnection; + $this->cookieName = $cookiename; + $this->boardUrl = $boardurl; + $this->loginPage = $loginpage; + $this->dbConnection = $dbconnection; $this->requestStack = $requestStack; $this->container = $container; } @@ -65,7 +69,7 @@ public function __construct($cookiename, $boardurl, $loginpage, $dbconnection, R * @param TokenInterface $token * @param UserProviderInterface $userProvider * @param $providerKey - * @return AnonymousToken + * @return null| */ public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) { @@ -79,17 +83,31 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ ); } - $session_id = $this->requestStack->getCurrentRequest()->cookies->get($this->cookiename . '_sid'); + $sessionId = $this->requestStack->getCurrentRequest()->cookies->get($this->cookieName . '_sid'); - if (empty($session_id)) + if (empty($sessionId)) { return null; // We can't authenticate if no SID is available. } /** @var EntityManager $em */ - $em = $this->container->get('doctrine')->getManager($this->dbconnection); + $em = $this->container->get('doctrine')->getManager($this->dbConnection); + + /** @var Session $session */ + $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($sessionId); + + if (!$session || $session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) { + return null; + } + + // We have a valid user, which is not the guest user. + + $roles = array(); + // @TODO: Assign roles. + + $token = new phpBBToken($token->getUser(), $providerKey, $roles); - $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($session_id); + return $token; } /** @@ -121,7 +139,7 @@ public function createToken(Request $request, $providerKey) */ public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - return new RedirectResponse($this->boardurl . $this->loginpage); + return new RedirectResponse($this->boardUrl . $this->loginPage); } } diff --git a/Entity/Session.php b/Entity/Session.php index fb26276..a26c340 100644 --- a/Entity/Session.php +++ b/Entity/Session.php @@ -65,7 +65,5 @@ public function setUser($user) $this->user = $user; } - - } From d0f39609bc8fb05412b4d963c4129b701c802254 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 15:58:34 +0200 Subject: [PATCH 09/16] Some more checks on the session --- Authentication/phpBBSessionAuthenticator.php | 11 +++++++++- Entity/Session.php | 23 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index 476e57f..fe616cd 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -84,6 +84,7 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ } $sessionId = $this->requestStack->getCurrentRequest()->cookies->get($this->cookieName . '_sid'); + $userId = $this->requestStack->getCurrentRequest()->cookies->get($this->cookieName . '_u'); if (empty($sessionId)) { @@ -96,10 +97,18 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ /** @var Session $session */ $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($sessionId); - if (!$session || $session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) { + + if (!$session || $session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) + { return null; } + if ($session->getUser()->getId() != $userId) + { + throw new \InvalidArgumentException("Incorrect session cookie found with username"); + } + // @TODO: IP validation. + // We have a valid user, which is not the guest user. $roles = array(); diff --git a/Entity/Session.php b/Entity/Session.php index a26c340..3414f96 100644 --- a/Entity/Session.php +++ b/Entity/Session.php @@ -33,6 +33,13 @@ class Session */ private $user; + /** + * @var string + * @ORM\Column(type="string", name="user_ip") + * + */ + private $ip; + /** * @return string */ @@ -65,5 +72,21 @@ public function setUser($user) $this->user = $user; } + /** + * @return string + */ + public function getIp() + { + return $this->ip; + } + + /** + * @param string $ip + */ + public function setIp($ip) + { + $this->ip = $ip; + } + } From 5c763e26097df683421890866f9240a48d4ce617 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 15:59:36 +0200 Subject: [PATCH 10/16] Get the session user --- Authentication/phpBBSessionAuthenticator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index fe616cd..14efa32 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -114,7 +114,7 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ $roles = array(); // @TODO: Assign roles. - $token = new phpBBToken($token->getUser(), $providerKey, $roles); + $token = new phpBBToken($session->getUser(), $providerKey, $roles); return $token; } From abe0ec850c5313e514a5f572e598df128b7922c4 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 16:05:33 +0200 Subject: [PATCH 11/16] Remove unused statements. --- Authentication/phpBBSessionAuthenticator.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index 14efa32..be743c3 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -17,11 +17,9 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authentication\SimplePreAuthenticatorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\User\UserProviderInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; @@ -69,7 +67,7 @@ public function __construct($cookiename, $boardurl, $loginpage, $dbconnection, * @param TokenInterface $token * @param UserProviderInterface $userProvider * @param $providerKey - * @return null| + * @return null|phpBBToken */ public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey) { @@ -105,7 +103,7 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ if ($session->getUser()->getId() != $userId) { - throw new \InvalidArgumentException("Incorrect session cookie found with username"); + throw new \InvalidArgumentException('Incorrect session cookie found with username'); } // @TODO: IP validation. From c62e3f668101cb150b7150e091d9db3753a845ff Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 17:24:05 +0200 Subject: [PATCH 12/16] Implement IP check. Code based on code from sessions.php from phpBB and simplified. --- Authentication/phpBBSessionAuthenticator.php | 75 +++++++++++++++++--- Entity/Session.php | 22 ++++++ 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index be743c3..9ca7f37 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -96,25 +96,43 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($sessionId); - if (!$session || $session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) + if ( + !$session || + $session->getUser() == null || + ($session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) || + $session->getUser()->getId() != $userId + ) { return null; } - if ($session->getUser()->getId() != $userId) + $userIp = $this->requestStack->getCurrentRequest()->getClientIp(); + + if (strpos($userIp, ':') !== false && strpos($session->getIp(), ':') !== false) + { + $s_ip = short_ipv6($session->getIp(), 3); + $u_ip = short_ipv6($userIp, 3); + } + else { - throw new \InvalidArgumentException('Incorrect session cookie found with username'); + $s_ip = implode('.', array_slice(explode('.', $session->getIp()), 0, 3)); + $u_ip = implode('.', array_slice(explode('.', $userIp), 0, 3)); } - // @TODO: IP validation. - // We have a valid user, which is not the guest user. + // Assume session length of 3600 + if ($u_ip === $s_ip && $session->getTime() < time() - 3600 + 60) + { + // We have a valid user, which is not the guest user. + + $roles = array(); + // @TODO: Assign roles. - $roles = array(); - // @TODO: Assign roles. + $token = new phpBBToken($session->getUser(), $providerKey, $roles); - $token = new phpBBToken($session->getUser(), $providerKey, $roles); + return $token; + } + return null; - return $token; } /** @@ -148,5 +166,44 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio { return new RedirectResponse($this->boardUrl . $this->loginPage); } + + /** + * Returns the first block of the specified IPv6 address and as many additional + * ones as specified in the length paramater. + * If length is zero, then an empty string is returned. + * If length is greater than 3 the complete IP will be returned + * + * @copyright (c) phpBB Limited + * @license GNU General Public License, version 2 (GPL-2.0) + * + * @param $ip + * @param $length + * @return mixed|string + */ + function short_ipv6($ip, $length) + { + if ($length < 1) + { + return ''; + } + + // extend IPv6 addresses + $blocks = substr_count($ip, ':') + 1; + if ($blocks < 9) + { + $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip); + } + if ($ip[0] == ':') + { + $ip = '0000' . $ip; + } + if ($length < 4) + { + $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length)); + } + + return $ip; + } + } diff --git a/Entity/Session.php b/Entity/Session.php index 3414f96..17ce645 100644 --- a/Entity/Session.php +++ b/Entity/Session.php @@ -40,6 +40,12 @@ class Session */ private $ip; + /** + * @var + * @ORM\Column(type="integer", name="session_time") + */ + private $time; + /** * @return string */ @@ -88,5 +94,21 @@ public function setIp($ip) $this->ip = $ip; } + /** + * @return mixed + */ + public function getTime() + { + return $this->time; + } + + /** + * @param mixed $time + */ + public function setTime($time) + { + $this->time = $time; + } + } From 4b92f38e4a8dd5eb0bd8d93661e680e00669d989 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 17:36:09 +0200 Subject: [PATCH 13/16] add ROLE_BOT check --- Authentication/phpBBSessionAuthenticator.php | 23 ++++++++++---------- Entity/User.php | 21 ++++++++++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index 9ca7f37..e409c2a 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -96,12 +96,10 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ $session = $em->getRepository('phpbbSessionsAuthBundle:Session')->findById($sessionId); - if ( - !$session || - $session->getUser() == null || - ($session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) || - $session->getUser()->getId() != $userId - ) + if (!$session || + $session->getUser() == null || + ($session->getUser() != null && $session->getUser()->getId() == self::ANONYMOUS) || + $session->getUser()->getId() != $userId) { return null; } @@ -110,8 +108,8 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ if (strpos($userIp, ':') !== false && strpos($session->getIp(), ':') !== false) { - $s_ip = short_ipv6($session->getIp(), 3); - $u_ip = short_ipv6($userIp, 3); + $s_ip = $this->shortIpv6($session->getIp(), 3); + $u_ip = $this->shortIpv6($userIp, 3); } else { @@ -125,7 +123,10 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ // We have a valid user, which is not the guest user. $roles = array(); - // @TODO: Assign roles. + + if ($session->getUser()->isBot()) { + $roles[] = 'ROLE_BOT'; + } $token = new phpBBToken($session->getUser(), $providerKey, $roles); @@ -175,12 +176,12 @@ public function onAuthenticationFailure(Request $request, AuthenticationExceptio * * @copyright (c) phpBB Limited * @license GNU General Public License, version 2 (GPL-2.0) - * + * * @param $ip * @param $length * @return mixed|string */ - function short_ipv6($ip, $length) + private function shortIpv6($ip, $length) { if ($length < 1) { diff --git a/Entity/User.php b/Entity/User.php index c5ae172..a5b973c 100644 --- a/Entity/User.php +++ b/Entity/User.php @@ -45,6 +45,12 @@ class User implements UserInterface */ private $password; + /** + * @var boolean + * @ORM\Column(type="integer", name="is_bot") + */ + private $bot; + /** * @var Role[] */ @@ -180,6 +186,21 @@ public function setSessions($sessions) $this->sessions = $sessions; } + /** + * @return boolean + */ + public function isBot() + { + return $this->bot; + } + + /** + * @param boolean $bot + */ + public function setBot($bot) + { + $this->bot = $bot; + } } From 40383cc3eef0e8ff04343bd10d0626f03b148c33 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 18:07:18 +0200 Subject: [PATCH 14/16] Add a first version of the Auth class, based on phpBB Auth --- Authentication/Auth.php | 197 ++++++++++++++++++ Authentication/phpBBSessionAuthenticator.php | 4 + .../phpbbSessionsAuthExtension.php | 6 + 3 files changed, 207 insertions(+) create mode 100644 Authentication/Auth.php diff --git a/Authentication/Auth.php b/Authentication/Auth.php new file mode 100644 index 0000000..19d5a0d --- /dev/null +++ b/Authentication/Auth.php @@ -0,0 +1,197 @@ + + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpBB\SessionsAuthBundle\Authentication; + +/** + * Permission/Auth class + * + * This is a copy based on the original phpBB Auth class. It has been modified to work with + * Doctrine entities for the ACL_OPTIONS_TABLE, and all unneeded stuff has been removed. + * + * This version does not support recreating the user_permissions field in the user table. + * If there is no user_permissions field, it will result in a exception. + * + */ +class auth +{ + var $acl = array(); + var $cache = array(); + var $acl_options = array(); + var $acl_forum_ids = false; + + /** + * Init permissions + * @param $user_permissions string + * @throws \Exception + */ + public function acl($user_permissions) + { + $this->acl = $this->cache = $this->acl_options = array(); + $this->acl_forum_ids = false; + + if (($this->acl_options = $cache->get('_acl_options')) === false) + { + $sql = 'SELECT auth_option_id, auth_option, is_global, is_local + FROM ' . ACL_OPTIONS_TABLE . ' + ORDER BY auth_option_id'; + $result = $db->sql_query($sql); + + $global = $local = 0; + $this->acl_options = array(); + while ($row = $db->sql_fetchrow($result)) + { + if ($row['is_global']) + { + $this->acl_options['global'][$row['auth_option']] = $global++; + } + + if ($row['is_local']) + { + $this->acl_options['local'][$row['auth_option']] = $local++; + } + + $this->acl_options['id'][$row['auth_option']] = (int) $row['auth_option_id']; + $this->acl_options['option'][(int) $row['auth_option_id']] = $row['auth_option']; + } + $db->sql_freeresult($result); + + $cache->put('_acl_options', $this->acl_options); + } + + if (!trim($user_permissions)) + { + throw new \Exception("We require user_permissions set by phpBB."); + } + + // Fill ACL array + $this->_fill_acl($user_permissions); + + // Verify bitstring length with options provided... + $renew = false; + $global_length = sizeof($this->acl_options['global']); + $local_length = sizeof($this->acl_options['local']); + + // Specify comparing length (bitstring is padded to 31 bits) + $global_length = ($global_length % 31) ? ($global_length - ($global_length % 31) + 31) : $global_length; + $local_length = ($local_length % 31) ? ($local_length - ($local_length % 31) + 31) : $local_length; + + // You thought we are finished now? Noooo... now compare them. + foreach ($this->acl as $forum_id => $bitstring) + { + if (($forum_id && strlen($bitstring) != $local_length) || (!$forum_id && strlen($bitstring) != $global_length)) + { + $renew = true; + break; + } + } + + // If a bitstring within the list does not match the options, we have a user with incorrect permissions set and need to renew them + if ($renew) + { + throw new \Exception('Renewing is not supported.'); + } + + return; + } + + /** + * Fill ACL array with relevant bitstrings from user_permissions column + * @param $user_permissions + */ + private function _fill_acl($user_permissions) + { + $seq_cache = array(); + $this->acl = array(); + $user_permissions = explode("\n", $user_permissions); + + foreach ($user_permissions as $f => $seq) + { + if ($seq) + { + $i = 0; + + if (!isset($this->acl[$f])) + { + $this->acl[$f] = ''; + } + + while ($subseq = substr($seq, $i, 6)) + { + if (isset($seq_cache[$subseq])) + { + $converted = $seq_cache[$subseq]; + } + else + { + $converted = $seq_cache[$subseq] = str_pad(base_convert($subseq, 36, 2), 31, 0, STR_PAD_LEFT); + } + + // We put the original bitstring into the acl array + $this->acl[$f] .= $converted; + $i += 6; + } + } + } + } + + /** + * Look up an option + * if the option is prefixed with !, then the result becomes negated + * + * If a forum id is specified the local option will be combined with a global option if one exist. + * If a forum id is not specified, only the global option will be checked. + * @param $opt string option + * @param int $f int forum_id + * @return bool + */ + function acl_get($opt, $f = 0) + { + $negate = false; + + if (strpos($opt, '!') === 0) + { + $negate = true; + $opt = substr($opt, 1); + } + + if (!isset($this->cache[$f][$opt])) + { + // We combine the global/local option with an OR because some options are global and local. + // If the user has the global permission the local one is true too and vice versa + $this->cache[$f][$opt] = false; + + // Is this option a global permission setting? + if (isset($this->acl_options['global'][$opt])) + { + if (isset($this->acl[0])) + { + $this->cache[$f][$opt] = $this->acl[0][$this->acl_options['global'][$opt]]; + } + } + + // Is this option a local permission setting? + // But if we check for a global option only, we won't combine the options... + if ($f != 0 && isset($this->acl_options['local'][$opt])) + { + if (isset($this->acl[$f]) && isset($this->acl[$f][$this->acl_options['local'][$opt]])) + { + $this->cache[$f][$opt] |= $this->acl[$f][$this->acl_options['local'][$opt]]; + } + } + } + + // Founder always has all global options set to true... + return ($negate) ? !$this->cache[$f][$opt] : $this->cache[$f][$opt]; + } +} \ No newline at end of file diff --git a/Authentication/phpBBSessionAuthenticator.php b/Authentication/phpBBSessionAuthenticator.php index e409c2a..64e618c 100644 --- a/Authentication/phpBBSessionAuthenticator.php +++ b/Authentication/phpBBSessionAuthenticator.php @@ -127,6 +127,10 @@ public function authenticateToken(TokenInterface $token, UserProviderInterface $ if ($session->getUser()->isBot()) { $roles[] = 'ROLE_BOT'; } + else + { + + } $token = new phpBBToken($session->getUser(), $providerKey, $roles); diff --git a/DependencyInjection/phpbbSessionsAuthExtension.php b/DependencyInjection/phpbbSessionsAuthExtension.php index 26099e2..c72e453 100644 --- a/DependencyInjection/phpbbSessionsAuthExtension.php +++ b/DependencyInjection/phpbbSessionsAuthExtension.php @@ -36,6 +36,12 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('phpbb_sessions_auth.database.boardurl', $config['session']['boardurl']); $container->setParameter('phpbb_sessions_auth.database.loginpage', $config['session']['loginpage']); + + // Yes, Yes, These defines are needed for Auth (From phpBB) + define('ACL_NEVER', 0); + define('ACL_YES', 1); + define('ACL_NO', -1); + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('services.yml'); } From 29ac95b166d0c2f4189acbc0a188e0176b2580c9 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 18:17:10 +0200 Subject: [PATCH 15/16] Some coding style things. --- Authentication/Auth.php | 81 ++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/Authentication/Auth.php b/Authentication/Auth.php index 19d5a0d..ccabee3 100644 --- a/Authentication/Auth.php +++ b/Authentication/Auth.php @@ -25,10 +25,10 @@ */ class auth { - var $acl = array(); - var $cache = array(); - var $acl_options = array(); - var $acl_forum_ids = false; + private $acl = array(); + private $cache = array(); + private $aclOptions = array(); + private $aclForumIds = false; /** * Init permissions @@ -37,54 +37,57 @@ class auth */ public function acl($user_permissions) { - $this->acl = $this->cache = $this->acl_options = array(); - $this->acl_forum_ids = false; + $this->acl = array(); + $this->cache = array(); + $this->aclOptions = array(); + $this->aclForumIds = false; - if (($this->acl_options = $cache->get('_acl_options')) === false) + if (($this->aclOptions = $cache->get('_acl_options')) === false) { $sql = 'SELECT auth_option_id, auth_option, is_global, is_local FROM ' . ACL_OPTIONS_TABLE . ' ORDER BY auth_option_id'; $result = $db->sql_query($sql); - $global = $local = 0; - $this->acl_options = array(); + $global = 0; + $local = 0; + $this->aclOptions = array(); while ($row = $db->sql_fetchrow($result)) { if ($row['is_global']) { - $this->acl_options['global'][$row['auth_option']] = $global++; + $this->aclOptions['global'][$row['auth_option']] = $global++; } if ($row['is_local']) { - $this->acl_options['local'][$row['auth_option']] = $local++; + $this->aclOptions['local'][$row['auth_option']] = $local++; } - $this->acl_options['id'][$row['auth_option']] = (int) $row['auth_option_id']; - $this->acl_options['option'][(int) $row['auth_option_id']] = $row['auth_option']; + $this->aclOptions['id'][$row['auth_option']] = (int) $row['auth_option_id']; + $this->aclOptions['option'][(int) $row['auth_option_id']] = $row['auth_option']; } $db->sql_freeresult($result); - $cache->put('_acl_options', $this->acl_options); + $cache->put('_acl_options', $this->aclOptions); } if (!trim($user_permissions)) { - throw new \Exception("We require user_permissions set by phpBB."); + throw new \Exception('We require user_permissions set by phpBB.'); } // Fill ACL array $this->_fill_acl($user_permissions); // Verify bitstring length with options provided... - $renew = false; - $global_length = sizeof($this->acl_options['global']); - $local_length = sizeof($this->acl_options['local']); + $renew = false; + $global_length = sizeof($this->aclOptions['global']); + $local_length = sizeof($this->aclOptions['local']); // Specify comparing length (bitstring is padded to 31 bits) $global_length = ($global_length % 31) ? ($global_length - ($global_length % 31) + 31) : $global_length; - $local_length = ($local_length % 31) ? ($local_length - ($local_length % 31) + 31) : $local_length; + $local_length = ($local_length % 31) ? ($local_length - ($local_length % 31) + 31) : $local_length; // You thought we are finished now? Noooo... now compare them. foreach ($this->acl as $forum_id => $bitstring) @@ -107,15 +110,15 @@ public function acl($user_permissions) /** * Fill ACL array with relevant bitstrings from user_permissions column - * @param $user_permissions + * @param $userPermissions */ - private function _fill_acl($user_permissions) + private function _fill_acl($userPermissions) { - $seq_cache = array(); - $this->acl = array(); - $user_permissions = explode("\n", $user_permissions); + $seq_cache = array(); + $this->acl = array(); + $userPermissions = explode("\n", $userPermissions); - foreach ($user_permissions as $f => $seq) + foreach ($userPermissions as $f => $seq) { if ($seq) { @@ -134,12 +137,14 @@ private function _fill_acl($user_permissions) } else { - $converted = $seq_cache[$subseq] = str_pad(base_convert($subseq, 36, 2), 31, 0, STR_PAD_LEFT); + $result = str_pad(base_convert($subseq, 36, 2), 31, 0, STR_PAD_LEFT); + $converted = $result; + $seq_cache[$subseq] = $result; } // We put the original bitstring into the acl array $this->acl[$f] .= $converted; - $i += 6; + $i += 6; } } } @@ -152,46 +157,46 @@ private function _fill_acl($user_permissions) * If a forum id is specified the local option will be combined with a global option if one exist. * If a forum id is not specified, only the global option will be checked. * @param $opt string option - * @param int $f int forum_id + * @param int $forumId int forum_id * @return bool */ - function acl_get($opt, $f = 0) + public function aclGet($opt, $forumId = 0) { $negate = false; if (strpos($opt, '!') === 0) { $negate = true; - $opt = substr($opt, 1); + $opt = substr($opt, 1); } - if (!isset($this->cache[$f][$opt])) + if (!isset($this->cache[$forumId][$opt])) { // We combine the global/local option with an OR because some options are global and local. // If the user has the global permission the local one is true too and vice versa - $this->cache[$f][$opt] = false; + $this->cache[$forumId][$opt] = false; // Is this option a global permission setting? - if (isset($this->acl_options['global'][$opt])) + if (isset($this->aclOptions['global'][$opt])) { if (isset($this->acl[0])) { - $this->cache[$f][$opt] = $this->acl[0][$this->acl_options['global'][$opt]]; + $this->cache[$forumId][$opt] = $this->acl[0][$this->aclOptions['global'][$opt]]; } } // Is this option a local permission setting? // But if we check for a global option only, we won't combine the options... - if ($f != 0 && isset($this->acl_options['local'][$opt])) + if ($forumId != 0 && isset($this->aclOptions['local'][$opt])) { - if (isset($this->acl[$f]) && isset($this->acl[$f][$this->acl_options['local'][$opt]])) + if (isset($this->acl[$forumId]) && isset($this->acl[$forumId][$this->aclOptions['local'][$opt]])) { - $this->cache[$f][$opt] |= $this->acl[$f][$this->acl_options['local'][$opt]]; + $this->cache[$forumId][$opt] |= $this->acl[$forumId][$this->aclOptions['local'][$opt]]; } } } // Founder always has all global options set to true... - return ($negate) ? !$this->cache[$f][$opt] : $this->cache[$f][$opt]; + return ($negate) ? !$this->cache[$forumId][$opt] : $this->cache[$forumId][$opt]; } } \ No newline at end of file From 79cb8f3d670ffda51cf526452fa2880e2663dd44 Mon Sep 17 00:00:00 2001 From: Paul Sohier Date: Sun, 31 May 2015 18:25:20 +0200 Subject: [PATCH 16/16] Some more coding style. --- Authentication/Auth.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Authentication/Auth.php b/Authentication/Auth.php index ccabee3..77ebab2 100644 --- a/Authentication/Auth.php +++ b/Authentication/Auth.php @@ -23,19 +23,19 @@ * If there is no user_permissions field, it will result in a exception. * */ -class auth +class Auth { - private $acl = array(); - private $cache = array(); - private $aclOptions = array(); + private $acl = array(); + private $cache = array(); + private $aclOptions = array(); private $aclForumIds = false; /** * Init permissions - * @param $user_permissions string + * @param $userPermissions string * @throws \Exception */ - public function acl($user_permissions) + public function acl($userPermissions) { $this->acl = array(); $this->cache = array(); @@ -72,13 +72,13 @@ public function acl($user_permissions) $cache->put('_acl_options', $this->aclOptions); } - if (!trim($user_permissions)) + if (!trim($userPermissions)) { throw new \Exception('We require user_permissions set by phpBB.'); } // Fill ACL array - $this->_fill_acl($user_permissions); + $this->_fill_acl($userPermissions); // Verify bitstring length with options provided... $renew = false;