Symfony 4 EasyAdminBundle: Problem with encoding and saving of Plain Password - php

these are my first steps with Symfony. I try to implement a simple user management with Easyadmin Bundle in Symfony 4.4. I followed the Tutorial on symfony.com and most of it is working correctly (Sign up form, backend login, backend security, backend listing of users from database).
My Problem is the creation and updating of a user in the Easyadmin backend. When I try to create a new user, I see the correct fields, I do enter some data and if I click "Save changes" it throws the following error:
An exception occurred while executing 'INSERT INTO app_users (username, email, roles, password, is_active) VALUES (?, ?, ?, ?, ?)' with params ["testname", "test#example.com", "a:1:{i:0;s:9:\"ROLE_USER\";}", null, 1]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null
Column 'password' cannot be null is pretty clear: I need to provide it with some encoded password string.
I think the data in the plain password field I enter is not encoded and/or not processed by the setPassword() method in my User entity.
As far as I understand some SO answers and the Symfony documentation it should work automagically!? I don't know. I tried to create an AdminController that extends EasyAdminController and hook it in somewhere in the persisting of the user entity, but I couldn't get it to work. (Something like this: https://stackoverflow.com/a/54749433)
How do I process/encode the plainpassword that it is saved to the password field in database?
User entity:
// /src/Entity/User.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* #ORM\Table(name="app_users")
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("username")
* #UniqueEntity("email")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=254, unique=true)
* #Assert\NotBlank(groups={"edit"})
*/
private $username;
/**
* #ORM\Column(type="string", length=254, unique=true)
* #Assert\NotBlank()
* #Assert\Email(groups={"edit"})
*/
private $email;
/**
* #ORM\Column(type="array")
*/
private $roles;
/**
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
public function __construct(){
$this->roles = array('ROLE_USER');
$this->isActive = true;
}
public function getId(){
return $this->id;
}
public function getUsername(){
return $this->username;
}
public function setUsername($username){
$this->username = $username;
}
public function getEmail(){
return $this->email;
}
public function setEmail($email){
$this->email = $email;
}
public function getIsActive(){
return $this->isActive;
}
public function setIsActive($is_active){
$this->isActive = $is_active;
}
public function getRoles(){
return $this->roles;
}
public function setRoles($roles){
$roles[] = 'ROLE_USER';
$this->roles = $roles;
}
public function getPlainPassword(){
return $this->plainPassword;
}
public function setPlainPassword($password){
$this->plainPassword = $password;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password){
$this->password = $password;
}
public function getSalt(){
return null;
}
public function eraseCredentials(){}
public function isAccountNonExpired(){
return true;
}
public function isAccountNonLocked(){
return true;
}
public function isCredentialsNonExpired(){
return true;
}
public function isEnabled(){
return $this->isActive;
}
/** #see \Serializable::serialize() */
public function serialize(){
return serialize(array(
$this->id,
$this->username,
$this->email,
$this->password,
$this->isActive,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized){
list (
$this->id,
$this->username,
$this->email,
$this->password,
$this->isActive,
) = unserialize($serialized, array('allowed_classes' => false));
}
}
Security.yaml:
# /config/packages/security.yaml
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
users_in_memory: { memory: null }
our_db_provider:
entity:
class: App\Entity\User
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
pattern: ^/
provider: our_db_provider
form_login:
login_path: login
check_path: login
default_target_path: account
always_use_default_target_path: true
csrf_token_generator: security.csrf.token_manager
logout:
path: /logout
target: /login
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/account, roles: ROLE_USER }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Easyadmin.yaml:
# /config/packages/easy_admin.yaml
easy_admin:
design:
menu:
- { entity: User, label: 'Benutzerverwaltung', icon: 'user' }
entities:
User:
class: App\Entity\User
label: 'Benutzer'
password_encoding: { algorithm: 'bcrypt', cost: 12 }
form:
form_options: { validation_groups: ['Default'] }
fields:
- { type: 'group', icon: 'address-card', label: 'Informationen', css_class: 'col-lg-6' }
- username
- email
- { type: 'group', icon: 'user-shield', label: 'Rechteverwaltung', css_class: 'col-lg-6' }
- { property: 'is_active', type: 'checkbox' }
- { property: 'roles', type: 'choice', type_options: { multiple: true, choices: { 'ROLE_USER': 'ROLE_USER', 'ROLE_ADMIN': 'ROLE_ADMIN' } } }
- { type: 'group', icon: 'user-lock', label: 'Passwort', css_class: 'col-lg-6' }
- { property: 'plainPassword', type: 'text', type_options: { required: false } }

I can now answer my own question:
Solution: I simply forgot to reference the controller in Easyadmin routes:
# config/routes/easy_admin.yaml
easy_admin_bundle:
resource: 'App\Controller\AdminController'
prefix: /admin
type: annotation
and here is the complete controller for everyone with the same question:
// src/Controller/AdminController.php
namespace App\Controller;
use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;
class AdminController extends EasyAdminController
{
private $passwordEncoder;
public function __construct(UserPasswordEncoderInterface $passwordEncoder)
{
$this->passwordEncoder = $passwordEncoder;
}
private function encodeUserPlainPassword($user)
{
$plainPassword = $user->getPlainPassword();
if (!empty($plainPassword)) {
$encoded = $this->passwordEncoder->encodePassword($user, $plainPassword);
$user->setPassword($encoded);
}
}
public function persistEntity($user)
{
$this->encodeUserPlainPassword($user);
parent::persistEntity($user);
}
public function updateEntity($user)
{
$this->encodeUserPlainPassword($user);
parent::updateEntity($user);
}
}

Related

Jwt token invalid credentials in symfony 5.4

I am having a problem in generating jwt token in symfony using this package
lexik/LexikJWTAuthenticationBundle . I followed carefully the documentation and have an invalid credentials error ( credentials are correct )
this is my security.yaml
security:
enable_authenticator_manager: true
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check # or api_login_check as defined in config/routes.yaml
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
# username_path: username
# password_path: password
api:
pattern: ^/api
methods: [POST]
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
and this is my user entity :
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #UniqueEntity(fields={"email"}, message="There is already an account with this email")
*/
#[ApiResource(formats: ["json"])]
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private $email;
#[ORM\Column(type: 'json')]
private $roles = [];
#[ORM\Column(type: 'string')]
private $password;
private $plainPassword;
/**
* #return mixed
*/
public function getPlainPassword()
{
return $this->plainPassword;
}
/**
* #param mixed $plainPassword
*/
public function setPlainPassword($plainPassword): void
{
$this->plainPassword = $plainPassword;
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* #deprecated since Symfony 5.3, use getUserIdentifier instead
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
$this->plainPassword = null;
}
}
now when i make the request from post man to get the token it tells me (the route is correct and well defined ) :
{
"code": 401,
"message": "Invalid credentials."
}
Your problem is the configuration. Just add them back and replace username_path: username with username_path: email.
json_login:
check_path: /api/login_check
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
Also add in the config/packages/lexik_jwt_authentication.yaml:
user_identity_field: email

Symfony __PHP_Incomplete_Class could not be converted to string

When I submit my login form I get that error. I read here that this could be caused by trying to
deserialze object without loaded class for that object
however because the code is hidden away within symfony internals I can't work out why a class wouldn't be loaded in that instance. Profiler seems to say that no queries have been performed, yet if I put in the wrong credentials it says "invalid credentials" so it must be accessing the DB correctly.
User class
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email", message="This email is already in use.")
* #UniqueEntity("username", message="This username is already in use")
*/
class User implements UserInterface, \Serializable
{
private $roles = "ROLE_USER";
/**
* #ORM\Column(name="salt",type="string", length=255)
*/
private $salt = "saltyboye";
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(name="username",type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(name = "password", type="string", length=255)
*/
private $password;
/**
* #ORM\Column(name="email", type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="datetime")
*/
private $registeredOn;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $referrer;
/**
* #ORM\Column(type="smallint")
*/
private $entries;
/**
* #ORM\Column(type="string", length=3)
*/
private $currency;
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->registeredOn,
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt));
}
public function unserialize($serialized)
{
list (
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt) = unserialize($serialized, array('allowed_classes' => false));
}
public function eraseCredentials()
{
}
public function getRoles()
{
return array("ROLE_USER");
}
public function getSalt()
{
return $this->salt;
}
public function getId()
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getRegisteredOn(): ?\DateTimeInterface
{
return $this->registeredOn;
}
public function setRegisteredOn(\DateTimeInterface $registeredOn): self
{
$this->registeredOn = $registeredOn;
return $this;
}
public function getReferrer(): ?int
{
return $this->referrer;
}
public function setReferrer(?int $referrer): self
{
$this->referrer = $referrer;
return $this;
}
public function getEntries(): ?smallint
{
return $this->entries;
}
public function setEntries($entries): self
{
$this->entries = $entries;
return $this;
}
public function setCurrency($currency): self
{
$this->currency = $currency;
return $this;
}
public function getCurrency(): ?string
{
return $this->currency;
}
}
Security.yaml
security:
hide_user_not_found: false
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: sha256
providers:
in_memory: { memory: ~ }
main_db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
# anonymous: true
pattern: ^/ #test
anonymous: ~
form_login:
login_path: login
check_path: login
csrf_token_generator: security.csrf.token_manager
provider: main_db_provider
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/$, roles: ROLE_USER }
# - { path: ^/$, roles: ROLE_USER }
The serialize function doesnot match the unserialize function! You need to change it:
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->registeredOn,
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt));
}
public function unserialize($serialized)
{
list (
$this->registeredOn, # <--- look here it is missing
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt) = unserialize($serialized, array('allowed_classes' => false));
}

Entity provider not working in security.yml php symfony3.4 ({"code":401,"message":"bad credentials"}.)

I'm using symfony 3.4 with DoctrineMongoDBBundle and LexikJWTAuthenticationBundle . I'm trying to create a user login which return JWT token. If i specify the username and password under in_memory provider, it returns the token but if i use a entity provider, it returns {"code":401,"message":"bad credentials"}.
Here is my security.yml
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
encoders:
AppBundle\Document\User:
algorithm: bcrypt
cost: 12
providers:
webprovider:
entity:
class: AppBundle\Document\User
property: username
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
provider: webprovider
form_login:
username_parameter: username
password_parameter: password
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: ~
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
#http_basic: ~
# https://symfony.com/doc/current/security/form_login_setup.html
#form_login: ~
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Here is my User class
<?php
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #MongoDB\Document(collection="users")
* #MongoDBUnique(fields="email")
*/
class User implements UserInterface
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\Field(type="string")
* #Assert\Email()
*/
protected $email;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $username;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $password;
/**
* #MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
Would really appreciate if someone could help, Thanks.
You should use FOS bundle components by extending Base model User calss in entity, better than using directly Implementing UserInterface, In your case problem could be your password is not getting encoded correct. It's better to encode using security.password_encoder. To more better understanding i am sharing an example to setup login and generating token.
Your security.yml should look like this
`# Symfony 3.4 security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_API: ROLE_USER, ROLE_MEDIC, ROLE_STUDENT
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
in_memory:
memory: ~
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login
stateless: true
anonymous: true
form_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/(api)
stateless: true #false (not assign cookies)
anonymous: ~
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
provider: fos_userbundle
access_control: .................`
Your User class should be like this:
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
/**
* #MongoDB\Document(collection="users")
* #MongoDBUnique(fields="email")
*/
class User implements BaseUser
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\Field(type="string")
* #Assert\Email()
*/
protected $email;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $username;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $password;
/**
* #MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
Now your Api tokenController.php to generate token and validate token
namespace \your_namespace\Api;
use FOS\RestBundle\Context\Context;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Document\User;
# Below most of the components belongs to Nelmio api or Sensio or Symfony
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
class TokenController extends FOSRestController
{
/**
* #Rest\Post("/tokens", name="api_token_new",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* #ApiDoc(
* section = "Security",
* description = "Get user token",
* parameters={
* {"name"="username", "dataType"="string", "required"=true, "description"="username or email"},
* {"name"="pass", "dataType"="string", "required"=true, "description"="password "},
* }
* )
*/
public function newTokenAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$username = $request->get('username');
/** #var User $user */
$user = $em->getRepository('AppBundle:User')->findOneBy(['email' => $username]);
if (!$user) {
throw $this->createNotFoundException();
}
if (!$user->isEnabled()){
throw $this->createNotFoundException($this->get('translator')->trans('security.user_is_disabled'));
}
$pass = $request->get('pass');
$isValid = $this->get('security.password_encoder')->isPasswordValid($user, $pass);
if (!$isValid) {
throw new BadCredentialsException();
}
$token = $this->get('lexik_jwt_authentication.encoder')->encode([
'username' => $user->getUsername(),
'id' => $user->getId(),
'roles' => $user->getRoles(),
'exp' => time() + (30 * 24 * 3600) // 30 days expiration -> move to parameters or config
]);
// Force login
$tokenLogin = new UsernamePasswordToken($user, $pass, "public", $user->getRoles());
$this->get("security.token_storage")->setToken($tokenLogin);
// Fire the login event
// Logging the user in above the way we do it doesn't do this automatically
$event = new InteractiveLoginEvent($request, $tokenLogin);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(['Public']);
$view->setContext($context);
return $this->handleView($view);
}
/**
* #Rest\Post("/validate", name="api_token_validate",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* #ApiDoc(
* section = "Security",
* description = "Get user by token",
* parameters={
* {"name"="token", "dataType"="textarea", "required"=true, "description"="token"},
* }
* )
*/
public function validateUserToken(Request $request)
{
$token = $request->get('token');
//get UserProviderInterface
$fos = $this->get('fos_user.user_provider.username_email');
//create PreAuthToken
$preAuthToken = new PreAuthenticationJWTUserToken($token);
try {
if (!$payload = $this->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
$preAuthToken->setPayload($payload);
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
//get user
/** #var User $user */
$user = $this->get('lexik_jwt_authentication.security.guard.jwt_token_authenticator')->getUser($preAuthToken, $fos);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(array_merge(['Public'],$user->getRoles()));
$view->setContext($context);
return $this->handleView($view);
}
if you face any problem then , let me know.
you need to add the provider before json_login tag this the case for me
provider: fos_userbundle

Symfony attribute expects a integer, but got string error

I am trying to do my own authentication class.
Here is my User entity.
<?php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class User
{
/**
* #ORM\Column(type="int", length="11")
*/
protected $id;
/**
* #ORM\Column(type="string", length="25")
*/
protected $login;
/**
* #ORM\Column(type="string", length="25")
*/
protected $password;
/**
* #ORM\Column(type="string", length="25")
*/
protected $firstName;
/**
* #ORM\Column(type="string", length="25")
*/
protected $lastName;
/**
* #ORM\Column(type="string", length="25")
*/
protected $email;
public function getId()
{
return $this->id;
}
public function getLogin()
{
return $this->login;
}
public function getPassword()
{
return $this->password;
}
public function getFirstName()
{
return $this->firstName;
}
public function getLastName()
{
return $this->lastName;
}
public function getEmail()
{
return $this->email;
}
public function setLogin($login)
{
$this->login = $login;
}
public function setPassword($password)
{
$this->password = $password;
}
public function setFirstName($firstName)
{
$this->firstName = $firstName;
}
public function setLastName($lastName)
{
$this->lastName = $lastName;
}
public function setEmail($email)
{
$this->email = $email;
}
}
And the security settings (just like in docs)
security:
encoders:
AppBundle\Entity\User:
algorithm: sha512
encode-as-base64: true
iterations: 10
providers:
main:
entity: { class: AppBundle:User, property: login }
firewalls:
main:
pattern: /.*
form_login:
check_path: /account/check
login_path: /account/login
logout: true
security: true
anonymous: true
access_control:
- { path: /admin/.*, role: ROLE_ADMIN }
- { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
I am getting next error - [Type Error] Attribute "length" of #ORM\Column declared on property AppBundle\Entity\User::$id expects a(n) integer, but got string.
Im not sure i can understand the error. From where it got string? I dont even have anything in users table.
I would like to ask you to help me to solve this.
Thanks
You're passing it a string by enclosing it in quotes. I'm suspect you think it's similar to HTML where you do need to enclose attributes in quotes - that is not the case here:
class User
{
/**
* #ORM\Column(type="int", length=11)
*/
protected $id;
//...
}
Apply this change everything you used length="11"
If I'm not wrong type should be integer and you don't need the length. So something like
/**
* #ORM\Column(type="integer")
*/
protected $id;

Symfony 2.6 FR3DLdapBundle. Authentication request could not be processed due to a system

Hi everybody and I m sorry if the question is dumb. I am very new with Symfony (2.6) and my first project requires the User to authenticate from AD.
Whatever I do I keep getting: Authentication request could not be processed due to a system problem.
security.yml
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
chain_provider:
chain:
providers: [fos_userbundle, fr3d_ldapbundle]
fr3d_ldapbundle:
id: fr3d_ldap.security.user.provider
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
fr3d_ldap: ~
form_login:
# check_path: fos_user_security_check
# login_path: fos_user_security_login
always_use_default_target_path: true
default_target_path: /main
provider: chain_provider
logout:
path: fos_user_security_logout
target: /login
anonymous: ~
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
config.yml
fr3d_ldap:
driver:
host: 192.168.137.200
port: 50000
username: DOMAIN\Admnistrator # Optional
password: pass_here # Optional
user:
baseDn: OU=HP,OU=MANA,DC=DOMAIN,DC=com
filter: (&(ObjectClass=Person))
attributes: # Specify ldap attributes mapping [ldap attribute, user object method]
- { ldap_attr: uid, user_method: setUsername }
- { ldap_attr: mail, user_method: setEmail }
Entity/User class
/**
* #ORM\Entity
* #ORM\Table(name="mdr_user")
*/
class User extends BaseUser implements LdapUserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $name;
/**
* Ldap Object Distinguished Name
* #ORM\Column(type="string", length=128)
* #var string $dn
*/
private $dn;
public function __construct()
{
parent::__construct();
if (empty($this->roles)) {
$this->roles[] = 'ROLE_USER';
}
}
public function setName($name) {
$this->name = $name;
}
/**
* {#inheritDoc}
*/
public function setDn($dn)
{
$this->dn = $dn;
}
/**
* {#inheritDoc}
*/
public function getDn()
{
return $this->dn;
}
}
if I dont implement LdapUserInterface, the DB authenticates fine but always if I use anything else other than mysql entries I get that error. Can you please help me with that ?
Appreciate it.
Try to do this on your loginAction()
\Doctrine\Common\Util\Debug::dump($this->getDoctrine()->getEntityManager()->find('AppBundle:User', 1));
You might possible see an error. This works for me.
What fixed this for me was adding:
implements UserInterface, \Serializable
to the end of my entity's class declaration then adding the required methods to the entity at the bottom:
/**
* #return null
*/
public function getSalt(){
return null;
}
/**
* #return array
*/
public function getRoles(){
return array('ROLE_USER');
}
public function eraseCredentials(){
}
/**
* #return string
*/
public function serialize(){
return serialize(array($this->id, $this->username, $this->password));
}
/**
* #param string $serialized
*/
public function unserialize($serialized) {
list($this->id, $this->username,$this->password) = unserialize($serialized);
}

Categories