Authenticated: No appears in the Symfony2 dev toolbar after a successful login.
In my success handler I can access $token->getRoles() and see the role objects assigned to the user so it appears to be serializing okay.
So I'm not sure why it's not authenticating.
Here is my security.yml:
security:
encoders:
FixedApp\Model\User:
algorithm: sha1
encode_as_base64: false
iterations: 1
role_hierarchy:
ROLE_ADMIN: [ROLE_USER, ROLE_LIMITED_ADMIN]
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
administrators:
entity: { class: FixedApp\Model\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt|error)|css|images|js)/
security: false
login:
pattern: ^/$
security: false
secured_area:
pattern: ^/
form_login:
check_path: fixed_app_authentication_login
login_path: fixed_app_homepage
username_parameter: form[username]
password_parameter: form[password]
default_target_path: fixed_app_hub_homepage
always_use_default_target_path: true
success_handler: security.authentication.success_handler
logout:
path: fixed_app_authentication_logout
target: fixed_app_homepage
access_control:
- { path: ^/log-in$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /users/edit, roles: ROLE_ADMIN }
It not authenticating is a problem, because when I go to /users/edit as an admin it says Access Denied. So I need to figure out what is going on here. Any ideas would be most appreciated.
I saw a number of other people online with this same problem but I've never seen a solution put anywhere before - so hopefully this helps someone.
In UserRole.php class I was missing this function:
/**
* #see RoleInterface
*/
public function getRole()
{
return $this->role;
}
And secondly, in User.php class I made it implement EquatableInterface:
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
...
class User implements AdvancedUserInterface, EquatableInterface, \Serializable
{
...
public function isEqualTo(UserInterface $user)
{
if ($this->getId() == $user->getId())
{
return true;
}
else
{
return false;
}
}
And then it started working. The Symfony toolbar button went green, it says Authenticated: Yes and it lists all the roles for that user.
Related
I'm working with symfony at backend (api). The authentication process is handled by FosUserBundle, LexikJWTAuthenticationBundle and LdapTools... all works fine.
The problem is when I'm about to get the Authenticated user in a controller or service.
The user is authenticated by the Authorization header, does not exist 401 Exception
$this->container->get('security.token_storage')->getToken()->getUser()//null
$preAuthToken = $this->container->get('security.token_storage')->getToken();
$tmp = $this->container->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken);//i can get the username and roles
But the real proble is with the security system
if ($this->isGranted('ROLE_USER')) {
echo 'never gets here!!';
} else {
echo 'always';
}
The security system always fails because the user returned by getUser() is always null.
My question is: the LexikJWTAuthenticationBundle should not inject or replace the current user, token after a successfull authentication?
or should I do it programatically? I dont want to fall in bad practices..
thanks in advance!
security.yml info
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
LdapTools\Bundle\LdapToolsBundle\Security\User\LdapUser: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
ldap:
id: ldap_tools.security.user.ldap_user_provider
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
refresh:
pattern: ^/api/token/refresh
stateless: true
anonymous: true
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/login
stateless: true
provider: fos_userbundle
anonymous: true
form_login:
check_path: /login
require_previous_session: false
username_parameter: username
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: AppBundle\Handler\AuthenticationFailureHandler
require_previous_session: false
guard:
authenticators:
- ldap_tools.security.ldap_guard_authenticator
logout: true
api:
pattern: ^/
stateless: true
lexik_jwt: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: IS_AUTHENTICATED_FULLY }
Auth. failure handler(just in case)
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$token = $exception->getToken();
if (is_string($exception->getToken()->getUser())) {
$usuario = $this->container->get('fos_user.user_manager')->findUserByUsername($token->getUsername());
if ($usuario) {
$token = new UsernamePasswordToken($usuario, 'yes', 'public', $usuario->getRoles());
} else {
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
}
return $this->handleAuthenticationFail($request, $token, $exception);
}
public function handleAuthenticationFail(Request $request, TokenInterface $token, AuthenticationException $exception)
{
$username = $token->getUsername();
$password = $request->get('password');
if ($this->ldapManager->authenticate($username, $password)) {
return $this->container->get('lexik_jwt_authentication.handler.authentication_success')->handleAuthenticationSuccess($token->getUser());
}
return $this->container->get('lexik_jwt_authentication.handler.authentication_failure')->onAuthenticationFailure($request, $exception);
}
With sf3
symfony 3 password can be checked but with a static dn
dn_string: 'uid={username},dc=example,dc=com'
So I created my own UserAuthenticationProvider class
But now I don't know how to declare it in my security.yml ?
security.yml
providers:
your_db_provider:
entity:
class: AcSecurityBundle:User
property: username
fos_userbundle:
id: fos_user.user_provider.username
secured_area:
pattern: ^/
anonymous: true
provider: your_db_provider
form_login:
csrf_token_generator: security.csrf.token_manager
use_referer: true
form_login_ldap:
service: ldap
use_referer: true
logout: true
AcSecurityBundle/AcUserAuthenticationProvider.php
class AcUserAuthenticationProvider extends UserAuthenticationProvider
{
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 {
$query = "(sAMAccountName=$username)";
$search_dn = "CN=app,OU=apps,DC=domain,DC=be";
$search_password = "mdp";
$this->ldap->bind($search_dn, $search_password);
$t = $this->ldap->query("OU=At,OU=user,DC=domain,DC=be", $query);
$entry = $t->execute();
if ($entry->count() > 0) {
$dn = $entry[0]->getDn();
}
$this->ldap->bind($dn, $password);
} catch (ConnectionException $e) {
throw new BadCredentialsException('The presented password is invalid.');
}
}
}
Thank you
Since Symfony 3.1 Symfony uses a new ldap provider
Symfony\Component\Ldap\Ldap
Provider Example:
services:
app.ldap:
class: Symfony\Component\Ldap\Ldap
factory: [ 'Symfony\Component\Ldap\Ldap', 'create']
arguments:
- ext_ldap
- host: srv-host
#debug: true
The string associated to the adapter you wish to use. Only one is
supported for now, which is ext_ldap.
New Ldap provider by Symfony, explained.
Security Provider Example:
providers:
#webservice_provider:
#id: app.webservice_user_provider
company_ldap:
ldap:
service: app.ldap
base_dn: DC=example, DC=local
search_dn: "CN=AdminName,OU=YourAdminGroup,DC=example,DC=LOCAL"
search_password: 'AdminPass'
default_roles: ROLE_USER
Now you are allowed to look in your ldap system. If you set your
form_login_ldap right, your users can authenticate via your form
Security form_login_ldap Example:
form_login_ldap:
provider: company_ldap
service: app.ldap #system/provider your user authenticate to
dn_string: '{username}#example.local'
csrf_token_generator: security.csrf.token_manager
check_path: /login_check #make sure the check is behind the firewall
login_path: /login #make sure login IS_AUTHENTICATED_ANONYMOUSLY
logout:
path: /logout #logout path
target: /login #after logout redirect to login
anonymous: true
Access_control/firewall Example:
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/, role: ROLE_USER } #all pages require user to be logged in
Now you have your dynamic ldap login form.
Symfony login form setup.
Hope this helps you. Took me 8 days to figure this out by myself.
You don't need to do any of the above. You can use the DN string as:
dn_string: '{username}'
That's actually the default value according to the docs.This will work because AD supports several bind methods in addition to a full DN (such as the sAMAccountName or UPN).
I am using the FOSUserBundle and am successfully creating users in my database. However, I'm trying to log users in and only have ROLE_ADMIN users access /admin by following the Symfony security walk-through.
However, even without having figured out logging users in, when I try to access localhost/app_dev.php/admin I am able to access it as the "Anon" user. Below are my security.yml and controller files:
app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: security.csrf.token_manager
logout: true
anonymous: true
access_control:
- { path: ^/admin/, role: ROLE_ADMIN }
src/AppBundle/Controller/DefaultController.php
class DefaultController extends Controller
{
/**
* #Route("/admin", name="admin")
*/
public function AdminAction(Request $request)
{
return $this->render('default/admin.html.twig', array(
'title' => 'Welcome Admin!!'
));
}
}
I'm not familiar with FOSUserBundle however your access_control entry is for the path /admin/ - note the trailing slash whereas your example localhost/app_dev.php/admin doesn't. If you remove that from your access_control entry or change your routes then this should work as expected.
E.g.
access_control:
- { path: ^/admin, role: ROLE_ADMIN }
I don't understand my problem. I just want :
/ redirected /home
/home is not secured but logged user is able to navigate into
the whole website.
Non authenticated user is only able to see the homepage
People can register an account to access the whole website
So it's my security.yml config :
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
root:
pattern: ^/$
security: false
home:
pattern: ^/home$
security: false
login:
pattern: ^/login$
security: false
register:
pattern: ^/account/
security: false
secured_area:
pattern: ^/
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
Registration is ok, login too. But after the redirection to the homepage, user is not authenticated (in the symfony profiler "You are not authenticated."). If I reach the secured area, i'm logged but not authenticated.
<?php
namespace Siriru\AntBundle\Controller;
use Siriru\AntBundle\Form\Model\Registration;
use Siriru\AntBundle\Form\Type\RegistrationType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\SecurityExtraBundle\Annotation\Secure;
class AccountController extends Controller
{
/**
* #Route("/login", name="login")
* #Template()
*/
public function loginAction()
{
if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
/**
* #Route("/login_check", name="login_check")
*/
public function securityCheckAction()
{
// The security layer will intercept this request
}
/**
* #Route("/logout", name="logout")
*/
public function logoutAction()
{
// The security layer will intercept this request
}
/**
* #Route("/account/register", name="account_register")
* #Template()
*/
public function registerAction()
{
$form = $this->createForm(new RegistrationType(), new Registration());
return array('form' => $form->createView());
}
/**
* #Route("/account/create", name="account_create")
* #Template()
*/
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->bind($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$user = $registration->getUser();
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(), $user->getSalt());
$user->setPassword($password);
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('homepage'));
}
return $this->render('SiriruAntBundle:Account:register.html.twig', array('form' => $form->createView()));
}
}
I need some help =) Thank you.
Try to change your firewall configuration to catch all the urls, then set anonymous: ~ and use access_control to restrict all the urls to ROLE_USER.
The problem is that the security session is not shared by default between different firewalls.
Something like this should work:
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/home$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
After login failure, the application does not redirect to the login page.Here is my security.yml configuration:
security:
encoders:
Acme\SecurityBundle\Entity\Users: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
providers:
main:
entity: { class: Acme\SecurityBundle\Entity\Users}
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login$
security: false
secured_area:
pattern: ^/admin/
form_login:
check_path: /login_check
login_path: /login
logout:
path: /logout
target: /home
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
Here is my controller:
class LoginController extends Controller {
/**
* #Route("/login",name="login")
*/
public function loginAction() {
//displays login form and renders the login.html.twig
}
/**
* #Route("/login_check", name="login_check")
*/
public function loginCheckAction() {
// The security layer will intercept this request
}
After login failure I get error as :
The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
500 Internal Server Error - LogicException
I tried the login section inside the Demo that ships with symfony. I found the same result.
What modification should I have to make in order redirect to login form after login failure ?
You don't need to create a controller class for the login and logout actions. You just need to specify those routes inside of routing.yml, something like:
login_path:
pattern: /login
login_check_path:
pattern: /admin/login_check
logout_path:
pattern: /admin/logout
These routes are automatically handled by the security component.
In the firewall configuration, your check_path should be defined as /admin/login_check. The logout path should be /admin/logout.