Using LexikJWTAuthenticationBundle, FOSRest, FOSUser how do I get authenticated user profile by token. Is it possible?
So let's say user is already authenticated via LexikJWT and I have an api endpoint like /api/profile where I send the token and I expect to get specified user data.
I'm using for frontend ReactJS with Redux.
This is an example of how to get your user by a service when the user is already authenticated:
class UserService
{
/** #var TokenStorageInterface */
private $tokenStorage;
/**
* #param TokenStorageInterface $storage
*/
public function __construct(
TokenStorageInterface $storage,
)
{
$this->tokenStorage = $storage;
}
public function getCurrentUser()
{
$token = $this->tokenStorage->getToken();
if ($token instanceof TokenInterface) {
/** #var User $user */
$user = $token->getUser();
return $user;
} else {
return null;
}
}
}
And in your services.yml:
tenant_user_service:
class: YourBundle\YourPackage\UserService
arguments: [ '#security.token_storage' ]
This will return your user - but be aware depending on the how user got set to the token during authentication this can be as well only your username as a string. But basically you get any content from your current $token->getUser().
i'm new but i can try...
you can use the annotation like
#Security("is_granted('ROLE_USER')")
in your controller and something like$this->getUser()->getUsername(); to get the username.
example:
$user = $this->get('doctrine.orm.default_entity_manager')
->getRepository('AppBundle:User')
->FindOne($this->getUser()->getUsername());`
after that you serialize datas, create new Response and return it.
Related
How can I fetch the currently logged in User from anywhere within the Backend code? For example I have an EventSubscriber class and want to fetch it from there.
How can I do that w/o the help of i.e. AbstractController?
Symfony AbstractController is the core of most Controllers. Including EasyAdmin crud controller (XXXCrudController) extends AbstractController so you can access the same methods.
One of those is getUser() which return the current logged in user.
* Get a user from the Security Token Storage.
*
* #return UserInterface|null
*
* #throws \LogicException If SecurityBundle is not available
*
* #see TokenInterface::getUser()
*/
protected function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application. Try running "composer require symfony/security-bundle".');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return null;
}
// #deprecated since 5.4, $user will always be a UserInterface instance
if (!\is_object($user = $token->getUser())) {
// e.g. anonymous authentication
return null;
}
return $user;
}
So when trying to get the logged used in a controller, just use this method.
If you want to get the same thing, but for example in a service, you can basically do the same as what the method actually does by using the service injection with TokenStorageInterface to access the TokenStorage service which can get the current user.
So in your event subscriber, add TokenStorageInterface in your constructor to use it to first get the token and then your user. You may have to add another check to see if there is an user logged in (by checking if there is a token for example)
//YourService.php
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
private $tokenStorage
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function yourMethod()
{
//get token then user
$user = $tokenStorage->getToken()->getUser();
}
I'm implementing a GET request like below. This route allows anonymous access, but I want to give more sensitive information when the user is authenticated.
[Controller]
/*
* #Route ("/api/item/{code}.{_format}", name="api.item.get", defaults={"_format"="json"}})
* #Method("GET")
* #Secure(roles="ROLE_API, IS_AUTHENTICATED_ANONYMOUSLY")
*/
public function getItemAction(Request $request, $code)
{
/* #var UsernamePasswordToken $token */
$token = $this->get('security.token_storage')->getToken();
$user = $token->getUser();
// snip
}
[security.yml]
- { path: '^/?api/items/[a-zA-Z0-9-]+', role: [ROLE_API, IS_AUTHENTICATED_ANONYMOUSLY] }
I'm expecting the $user is a User object when the user is authenticated but it's a string type and the value is "anon.". What am I missing?
I can't try it since I don't have symfony 2.8 anymore (side note: you should seriously consider updating your version!) but have you tried something like this?
if ($user instanceof User) {
// fetch those sensitive info
}
I need to authenticate a user against an LDAP server with custom logic.
I'm implementing a custom authentication system in Symfony 3.3, and built a custom authenticator named LoginFormAuthenticator that extends AbstractFormLoginAuthenticator, as per:
http://symfony.com/doc/current/security/guard_authentication.html
I need to check the username against a User entity in the database, then, depending on the type of user, either auth against a bcrypt password stored in the database, or against an external LDAP server.
Inside of the checkCredentials method, I can validate the password stored in the database successfully with:
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator {
...
public function checkCredentials($credentials, UserInterface $user)
{
...
// check password if the user is database user
if ($user->getApp() == 'DB') {
if ($this->passwordEncoder->isPasswordValid($user, $password)) {
return true;
}
}
// check LDAP server if LDAP user
if ($this->getApp() == 'LDAP') {
if ($this->unknownLdapService->check($user, $password)
{
return true;
}
...
I'm not clear on the correct way to check the username and password against the LDAP server with native symfony functionality.
If I change my config to use form_login_ldap (and not my new authenticator), it does in fact successfully auth against LDAP, although where it makes the call is obfuscated to me.
What service or class I should be using to query LDAP in place of unknownLdapService above?
The solution I ended up using here was that I first injected the existing Symfony ldap service into the constructor of my method. The ldap service is configured in services.yml the same way the Symfony docs configure it for the form_login_ldap provider.
/**
* LoginFormAuthenticator constructor.
* #param FormFactoryInterface $formFactory
* #param EntityManager $em
* #param RouterInterface $router
* #param SecureUserPasswordEncoder $passwordEncoder
* #param Ldap $ldap
*/
public function __construct(..., Ldap $ldap, ... )
{
...
$this->ldap = $ldap;
}
Then inside of my checkCredentials method, I called the ldap bind method:
public function checkCredentials($credentials, $userInterface $user)
...
$password = $credentials['_password'];
$login_format = 'DOMAIN\%s'; // this is the expected format in my case
$login_username = sprintf($login_format, $user);
...
try {
// try to bind with the username and provided password
$this->ldap->bind($login_username, $password);
} catch (\Symfony\Component\Ldap\Exception\ConnectionException $e) {
//return false;
throw new CustomUserMessageAuthenticationException('The submitted LDAP password is invalid.');
};
return true;
};
This works, and if the ldap auth fails, it throws the appropriate exception.
You can could use your own LDAP service: you just need to call ldap_bind. (it can allows you to do more ldap checks or to mock it aswell)
You could alose use the Symfony provider: vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authentication/Provider/LdapBindAuthenticationProvider.php
namespace Symfony\Component\Security\Core\Authentication\Provider;
class LdapBindAuthenticationProvider extends UserAuthenticationProvider
{
private $userProvider;
private $ldap;
private $dnString;
public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, LdapClientInterface $ldap, $dnString = '{username}', $hideUserNotFoundExceptions = true)
{
parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
$this->userProvider = $userProvider;
$this->ldap = $ldap;
$this->dnString = $dnString;
}
/**
* {#inheritdoc}
*/
protected function retrieveUser($username, UsernamePasswordToken $token)
{
if ('NONE_PROVIDED' === $username) {
throw new UsernameNotFoundException('Username can not be null');
}
return $this->userProvider->loadUserByUsername($username);
}
/**
* {#inheritdoc}
*/
protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
{
$username = $token->getUsername();
$password = $token->getCredentials();
if ('' === $password) {
throw new BadCredentialsException('The presented password must not be empty.');
}
try {
$username = $this->ldap->escape($username, '', LDAP_ESCAPE_DN);
$dn = str_replace('{username}', $username, $this->dnString);
$this->ldap->bind($dn, $password);
} catch (ConnectionException $e) {
throw new BadCredentialsException('The presented password is invalid.');
}
}
}
how do i get the password eneterd via a costum user provider. I found this question:
Symfony Security / Custom User Provider : How to get the login password within the custom User Provider?
Which says i would have to overrise the loadUserByName method, and add the password as a parameter, the issue is that i cannot find the file he overrides:
security_listeners.xml
Where is this file located?
I found the:
$user = $this->userProvider->loadUserByUsername($username);
call in the DOAAuthenticationProvider, and i see that the method takes:
protected function retrieveUser($username, UsernamePasswordToken $token)
as augments, i asume i need to pass the password there, and then pass it to loadUserByName, which calls my costum userprovider method.
Any help on how to achieve this is appreciated.
You can create an AuthenticationHandler on your bundle. and the on successful login fetch the password from the token.
in services.yml:
security.authentication.success_handler:
class: Wix\UserBundle\EventListener\AuthenticationHandler
arguments: ["#security.http_utils", {}]
tags:
- { name: 'monolog.logger', channel: 'security' }
And sample class:
class AuthenticationHandler extends DefaultAuthenticationSuccessHandler
{
/**
* This is called when an interactive authentication attempt succeeds. This
* is called by authentication listeners inheriting from
* AbstractAuthenticationListener.
*
* #param Request $request
* #param TokenInterface $token
*
* #return Response never null
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$response = new RedirectResponse('/');
$response->setStatusCode(200);
return $response;
}
}
I wanna write a custom Security handler and this will be a simple ACL which restrict data by user id. I don't want use a standart ACL, no need to use all functional and create aditional database with permissions.
So I create my new handler and now I recieve $object as Admin class. With Admin class I can restrict access to services but can't restrict any rows in service.
The question is how I can recieve Entities and check permission on Entities like this:
public function isGranted(AdminInterface $admin, $attributes, $object = null)
{
if ($object->getUserId()==5){
return true
}
}
Overwrite the security handler in sonata config:
sonata_admin:
title: "Admin"
security:
handler: custom.sonata.security.handler.role
Create your service:
custom.sonata.security.handler.role:
class: MyApp\MyBundle\Security\Handler\CustomRoleSecurityHandler
arguments:
- #security.context
- [ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_USER]
- %security.role_hierarchy.roles%
Last step, but not less important is to create your class, retrieve your user and based by his credentials allow/deny access:
/**
* Class CustomRoleSecurityHandler
*/
class CustomRoleSecurityHandler extends RoleSecurityHandler
{
protected $securityContext;
protected $superAdminRoles;
protected $roles;
/**
* #param \Symfony\Component\Security\Core\SecurityContextInterface $securityContext
* #param array $superAdminRoles
* #param $roles
*/
public function __construct(SecurityContextInterface $securityContext, array $superAdminRoles, $roles)
{
$this->securityContext = $securityContext;
$this->superAdminRoles = $superAdminRoles;
$this->roles = $roles;
}
/**
* {#inheritDoc}
*/
public function isGranted(AdminInterface $admin, $attributes, $object = null)
{
/** #var $user User */
$user = $this->securityContext->getToken()->getUser();
if ($user->hasRole('ROLE_ADMIN')){
return true;
}
// do your stuff
}
}