I've been playing a bit with socket.io's chat, and I have a question: how can I differentiate between an admin user and a regular user in the chat room? I'd like the admin to have powers like kicking and banning people, but my users to not.
I'm using Symfony to develop my application, and I'd like to use its user database for the chat users. I'm using FOSUserBundle for my Symfony app's users. They are split into multiple groups, so I have the admin
group, and others.
The admin
group has ROLE_ADMIN
which means that each user inside it has that role. That is the admins group and each user in this group should have permissions to ban, kick, mute etc. other users in the chat room.
For using my Symfony users in the chat, I've been reading into Redis to get their sessions, but I'm not exactly sure how to make the difference between my admin users and regular users. How can I prevent a regular user from making a request to the server that does something that the user doesn't have access to? Because anyone can do requests, but how can I validate those requests if they come from users stored in a MySQL database on the Apache server?
If not for Symfony, how this could be done in a regular PHP application? In the end, it doesn't matter how the admin is defined, but how to connect him to the Node server and how to make the Node server work with my user database.
I had an idea of simply encrypting and sending the user's data to the node server, then decrypt it there. Only the two servers know the private keys, so even if a client gets its hands on the encrypted data, he can't make a request with it for another client. I may do some IP check and a timestamp. The decrypted data on the node server could then be used to say if the user is an admin or not and allow him to send certain requests. Is this a good idea or is there a better way?
I had an idea of simply encrypting and sending the user's data to the node server, then decrypt it there. Only the two servers know the private keys, so even if a client gets its hands on the encrypted data, he can't make a request with it for another client.
That's the basic idea.
How I would do it? I would use something like JWT to just send the userId to the node application. Doesn't have to be encrypted, because I only care about the jwt signature to make sure the request was indeed issued by the real user.
After that, using the userId I would make a server side call to the php application to check the roles of the user.
To elaborate:
How to handle banning
I usually create a SecurityAccessManager
service to check user's roles with voters calls (I can provide an exemple if needed) to check specific rights like "is this user can update this specific post?"
Config
company.navigation.security_access:
class: Company\NavigationBundle\Services\SecurityAccessManager
arguments:
- @security.authorization_checker
- @security.token_storage
Service code
namespace Company\NavigationBundle\Services;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class SecurityAccessManager
{
private $authorizationChecker;
private $tokenStorage;
private $debug;
public function __construct(
AuthorizationCheckerInterface $authorizationChecker,
TokenStorage $tokenStorage)
{
$this->authorizationChecker = $authorizationChecker;
$this->tokenStorage = $tokenStorage;
$this->debug = true;
}
// *************************************************************************
// User
// *************************************************************************
public function getUser()
{
return $this->tokenStorage->getToken()->getUser();
}
public function getUserId()
{
return $this->tokenStorage->getToken()->getUser()->getId();
}
public function isAuthenticatedUser()
{
return $this->authorizationChecker->isGranted('IS_AUTHENTICATED_REMEMBERED');
}
// *************************************************************************
// Roles checker
// *************************************************************************
public function isAdmin()
{
if($this->authorizationChecker->isGranted('ROLE_ADMIN') !== true) {
return false;
} else {
return true;
}
}
public function checkRightAdmin()
{
if($this->authorizationChecker->isGranted('ROLE_ADMIN') !== true) {
throw new AccessDeniedException('Unauthorised access! '.($this->debug ? __FUNCTION__ : null));
}
return true;
}
public function checkUserHasRightToEditPost($postId)
{
// Check if user has right to modify the post
if ($this->authorizationChecker->isGranted('is_user_has_right_to_edit_post', $postId) === false) {
throw new AccessDeniedException('Unauthorised access! '.($this->debug ? __FUNCTION__ : null));
}
return true;
}
}
Then, in your controllers actions, you can check the user's rights
namespace Company\YourBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class YourBunbleController extends Controller
{
/**
* Get the service
* @return \Company\NavigationBundle\Services\SecurityAccessManager
*/
private function getService()
{
return $this->get('company.navigation.security_access');
}
public function updatePostAction(Request $request, $postId)
{
// Throw 403 if user has no admin rights
$this->getService()->checkRightAdmin();
// Throw 403 if user has no rights to update the post
$this->getService()->checkUserHasRightToEditPost();
//OK, you can update database
...
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With