When I switch as admin to a user site.loc/?_switch_user=user#email.com I don't get the role ROLE_PREVIOUS_ADMIN.
I'm not sure which code I should show you..
Symfony Version: symfony2 2.2.2
User.php
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->email
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->email
) = unserialize($serialized);
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
return
md5($user->getUsername()) == md5($this->getUsername()) &&
md5(serialize($user->getGroups())) == md5(serialize($this->getGroups()));
}
You should implement EquatableInterface for User class:
class User implements UserInterface, EquatableInterface, \Serializable {
// ...
public function isEqualTo(UserInterface $user) {
return $this->username === $user->getUsername();
}
public function serialize() {
return serialize(array(
$this->id,
$this->username,
));
}
public function unserialize($serialized) {
list($this->id, $this->username) = unserialize($serialized);
}
// ...
}
Related
I'm trying to create a Cart under Symfony 6.1.2
I want to add my product in my cart but i have this error message : Warning: Undefined variable $session
public function get()
{
//return $this->session->get('cart');
return $session->get('cart');
}
i've commented a line because i was using SessionInterface but this is not used anymore on Symfony 6.1.
I don't get where is my error so i'm there to have some help.
This is my Cart.php (with some commented lines from SessionInterface)
<?php
namespace App\Classe;
use Symfony\Component\HttpFoundation\RequestStack;
//use Symfony\Component\HttpFoundation\Session\SessionInterface;
class Cart
{
//private $session;
private $requestStack;
//public function __construct(SessionInterface $session)
public function __construct(RequestStack $requestStack)
{
//$this->session = $session;
$this->requestStack = $requestStack;
}
public function add($id)
{
$session = $this->requestStack->getSession();
$cart = $session->get('cart', []);
if (!empty($cart[$id])) {
$cart[$id]++;
} else {
$cart[$id] = 1;
}
$session->set('cart',$cart);
/* $cart = $this->session->get('cart', []);
if (!empty($cart[$id])) {
$cart[$id]++;
} else {
$cart[$id] = 1;
}
$this->session->set('cart', $cart); */
}
public function get()
{
//return $this->session->get('cart');
return $session->get('cart');
}
public function remove()
{
//return $this->session->remove('cart');
return $session->remove('cart');
}
}
and this is my CartController.php
<?php
namespace App\Controller;
use App\Classe\Cart;
use App\Entity\Product;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class CartController extends AbstractController
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* #Route("/mon-panier", name="cart")
*/
public function index(Cart $cart)
{
$cartComplete = [];
foreach ($cart->get() as $id => $quantity){
$cartComplete[] = [
'product' => $this->entityManager->getRepository(Product::class)-
>findOneById($id),
'quantity' => $quantity
];
}
return $this->render('cart/index.html.twig', [
'cart' => $cartComplete
]);
}
/**
* #Route("/cart/add/{id}", name="add_to_cart")
*/
public function add(Cart $cart, $id)
{
$cart->add($id);
return $this->redirectToRoute('cart');
}
/**
* #Route("/cart/remove/", name="remove_my_cart")
*/
public function remove(Cart $cart)
{
$cart->remove();
return $this->redirectToRoute('products');
}
}
If i miss something it would be kind to show me where is the mistake.
Thank you ;)
i found the solution.
With ResquestStack, i was wrong doing this :
public function get()
{
return $session->get('cart');
}
public function remove()
{
return $session->remove('cart');
}
I've changed my returns with :
public function get()
{
return $this->requestStack->getSession()->get('cart');
}
public function remove()
{
return $this->requestStack->getSession()->remove('cart');
}
}
Now everything is ok, in case of someone was block in this kind of situation
Im using system where it is using $user->is_admin and $user->is_employee and $user->is_customer there is no column is_admin or is_employee or is_customer in database. I know that it takes it from user model. but is_admin or is_employee is not defined anywhere. and dumping gives me true or false.
I want add new checking like is_manager. but cant find where I can add this..
Debugbar isnt showing any query for is_admin column..
Where it can be located?
example I have observer:
use App\Helper\SearchLog;
use App\User;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
class UserObserver
{
public function roleAttached(User $user, $role, $team)
{
if (!$user->is_admin) {
$type = 'Employee';
$route = 'admin.employee.edit';
if ($user->is_customer) {
$type = 'Customer';
$route = 'admin.customers.show';
}
SearchLog::createSearchEntry($user->id, $type, $user->name, $route);
SearchLog::createSearchEntry($user->id, $type, $user->email, $route);
}
}
I dont understand how it knows is_admin if it is not in database column?
My user model:
namespace App;
use App\Observers\UserObserver;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laratrust\Traits\LaratrustUserTrait;
class User extends Authenticatable
{
//------------------------------------ Traits ---------------------------
use LaratrustUserTrait;
use Notifiable;
//------------------------------------ Attributes ---------------------------
protected static function boot() {
parent::boot();
static::observe(UserObserver::class);
static::laratrustObserve(UserObserver::class);
}
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'password', 'remember_token',
];
protected $appends = [
'user_image_url', 'mobile_with_code', 'formatted_mobile'
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = ['deleted_at'];
//------------------------------------ Relations ----------------------------
public function employeeGroup() {
return $this->belongsTo(EmployeeGroup::class, 'group_id');
}
public function todoItems() {
return $this->hasMany(TodoItem::class);
}
public function completedBookings() {
return $this->hasMany(Booking::class, 'user_id')->where('bookings.status', 'completed');
}
public function booking() {
return $this->belongsToMany(Booking::class);
}
public function services() {
return $this->belongsToMany(BusinessService::class);
}
public function leave()
{
return $this->hasMany('App\Leave', 'employee_id', 'id');
}
public function role()
{
return $this->belongsToMany(Role::class);
}
public function employeeSchedule()
{
return $this->hasMany('App\EmployeeSchedules', 'employee_id', 'id');
}
//------------------------------------ Scopes -------------------------------
public function scopeAllAdministrators() {
return $this->whereHas('roles', function ($query) {
$query->where('name', 'administrator');
});
}
public function scopeAllCustomers() {
return $this->whereHas('roles', function ($query) {
$query->where('name', 'customer')->withoutGlobalScopes();
});
}
public function scopeOtherThanCustomers() {
return $this->whereHas('roles', function ($query) {
$query->where('name', '<>', 'customer');
});
}
public function scopeAllEmployees() {
return $this->whereHas('roles', function ($query) {
$query->where('name', 'employee');
});
}
//------------------------------------ Accessors ----------------------------
public function getUserImageUrlAttribute() {
if (is_null($this->image)) {
return asset('img/default-avatar-user.png');
}
return asset_url('avatar/' . $this->image);
}
public function getRoleAttribute() {
return $this->roles->first();
}
public function getMobileWithCodeAttribute() {
return substr($this->calling_code, 1).$this->mobile;
}
public function getFormattedMobileAttribute() {
if (!$this->calling_code) {
return $this->mobile;
}
return $this->calling_code.'-'.$this->mobile;
}
public function routeNotificationForNexmo($notification) {
return $this->mobile_with_code;
}
public function getIsAdminAttribute() {
return $this->hasRole('administrator');
}
public function getIsEmployeeAttribute() {
return $this->hasRole('employee');
}
public function getIsCustomerAttribute() {
if ($this->roles()->withoutGlobalScopes()->where('roles.name', 'customer')->count() > 0) {
return true;
}
return false;
}
//------------------------------------ Mutators -----------------------------
public function setPasswordAttribute($value) {
$this->attributes['password'] = bcrypt($value);
}
//------------------------------------ Formats -----------------------------
public function userBookingCount($date) {
return Booking::whereNull('deal_id')->where('user_id', $this->id)->whereDate('created_at', $date)->get()->count();
}
} /* end of class */
LoginController looks like this where is authenticated class:
protected function authenticated(Request $request, $user)
{
if ($user->is_admin || $user->is_employee) {
return redirect()->route('admin.dashboard');
}
if(!$user->is_admin && !$user->is_employee && Cookie::get('bookingDetails')!==null && Cookie::get('products')!==null && $this->checkUserBooking($user->id)>$this->settings->booking_per_day){
return redirect(route('front.index'))->withCookie(Cookie::forget('bookingDetails'))->withCookie(Cookie::forget('products'))->withCookie(Cookie::forget('couponData'));
}
return redirect(session()->get('url.encoded'));
}
You can make another accessor that will check if role is associated with current user entity.
public function getIsManagerAttribute() {
return $this->hasRole('manager');// presuming you have created manager role
}
Then you can check easily with
// $user = User::find(1);
// $user->is_manager;// true || false
I tried to create an authenticator for my login form, but I always am unlogged for some unclear reason.
[2016-10-05 18:54:53] security.INFO: Guard authentication successful! {"token":"[object] (Symfony\\Component\\Security\\Guard\\Token\\PostAuthenticationGuardToken: PostAuthenticationGuardToken(user=\"test#test.test\", authenticated=true, roles=\"ROLE_USER\"))","authenticator":"AppBundle\\Security\\Authenticator\\FormLoginAuthenticator"} []
[2016-10-05 18:54:54] security.INFO: An AuthenticationException was thrown; redirecting to authentication entry point. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExpiredException(code: 0): at /space/products/insurance/vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php:86)"} []
[2016-10-05 18:54:54] security.INFO: The security token was removed due to an AccountStatusException. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\AuthenticationExpiredException(code: 0): at /space/products/insurance/vendor/symfony/symfony/src/Symfony/Component/Security/Guard/Provider/GuardAuthenticationProvider.php:86)"} []
I don't understand this "AuthenticationExpiredException" as I have nothing stateless, nor any expiration in any way nowhere in my app.
Does this issue speak to anyone?
Edit 1
After a bunch of hours, it looks like I am unlogged because of the {{ is_granted('ROLE_USER') }} in Twig. Don't see why anyway.
Edit 2
If I dump() my security token on the onAuthenticationSuccess authenticator's method, authenticated = true.
But, If I dump() my security token after a redirect or when accessing a new page, 'authenticated' = false.
Why the hell my authentication isn't stored.
app/config/security.yml
security:
encoders:
AppBundle\Security\User\Member:
algorithm: bcrypt
cost: 12
providers:
members:
id: app.provider.member
role_hierarchy:
ROLE_ADMIN: "ROLE_USER"
firewalls:
dev:
pattern: "^/(_(profiler|wdt|error)|css|images|js)/"
security: false
main:
pattern: "^/"
anonymous: ~
logout: ~
guard:
authenticators:
- app.authenticator.form_login
access_control:
- { path: "^/connect", role: "IS_AUTHENTICATED_ANONYMOUSLY" }
- { path: "^/register", role: "IS_AUTHENTICATED_ANONYMOUSLY" }
- { path: "^/admin", role: "ROLE_ADMIN" }
- { path: "^/user", role: "ROLE_USER" }
- { path: "^/logout", role: "ROLE_USER" }
AppBundle/Controller/SecurityController.php
<?php
namespace AppBundle\Controller;
use AppBundle\Base\BaseController;
use AppBundle\Form\Type\ConnectType;
use AppBundle\Security\User\Member;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
class SecurityController extends BaseController
{
/**
* #Route("/connect", name="security_connect")
* #Template()
*/
public function connectAction(Request $request)
{
$connectForm = $this
->createForm(ConnectType::class)
->handleRequest($request)
;
return [
'connect' => $connectForm->createView(),
];
}
}
AppBundle/Form/Type/ConnectType.php
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type;
use Symfony\Component\Validator\Constraints;
use EWZ\Bundle\RecaptchaBundle\Form\Type\EWZRecaptchaType;
use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrue as RecaptchaTrue;
class ConnectType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', Type\EmailType::class, [
'label' => 'Your email',
'required' => true,
'constraints' => [
new Constraints\Length(['min' => 8])
],
])
->add('password', Type\PasswordType::class, [
'label' => 'Your password',
'constraints' => new Constraints\Length(['min' => 8, 'max' => 4096]), /* CVE-2013-5750 */
])
->add('recaptcha', EWZRecaptchaType::class, [
'label' => 'Please tick the checkbox below',
'constraints' => [
new RecaptchaTrue()
],
])
->add('submit', Type\SubmitType::class, [
'label' => 'Connect',
])
;
}
}
AppBundle/Security/Authenticator/FormLoginAuthenticator.php
<?php
namespace AppBundle\Security\Authenticator;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use AppBundle\Form\Type\ConnectType;
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
private $container; // ¯\_(ツ)_/¯
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getCredentials(Request $request)
{
if ($request->getPathInfo() !== '/connect') {
return null;
}
$connectForm = $this
->container
->get('form.factory')
->create(ConnectType::class)
->handleRequest($request)
;
if ($connectForm->isValid()) {
$data = $connectForm->getData();
return [
'username' => $data['email'],
'password' => $data['password'],
];
}
return null;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider->loadUserByUsername($credentials['username']);
}
public function checkCredentials($credentials, UserInterface $user)
{
$isValid = $this
->container
->get('security.password_encoder')
->isPasswordValid($user, $credentials['password'])
;
if (!$isValid) {
throw new BadCredentialsException();
}
return true;
}
protected function getLoginUrl()
{
return $this
->container
->get('router')
->generate('security_connect')
;
}
protected function getDefaultSuccessRedirectUrl()
{
return $this
->container
->get('router')
->generate('home')
;
}
}
AppBundle/Security/Provider/MemberProvider.php
<?php
namespace AppBundle\Security\Provider;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use AppBundle\Security\User\Member;
use Api\Gateway\RequestResponse\RequestResponseHandlerInterface;
use Api\Business\InsuranceWebsite\Action\GetInsuranceMember\GetInsuranceMemberRequest;
use Api\Gateway\Exception\NoResultException;
class MemberProvider implements UserProviderInterface
{
protected $gateway;
public function __construct(RequestResponseHandlerInterface $gateway)
{
$this->gateway = $gateway;
}
public function loadUserByUsername($username)
{
try {
$response = $this->gateway->handle(
new GetInsuranceMemberRequest($username)
);
} catch (NoResultException $ex) {
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
$member = new Member();
$member->setId($response->getId());
$member->setUsername($response->getEmail());
$member->setPassword($response->getPassword());
$member->setCompanyId($response->getCompanyId());
$member->setFirstname($response->getFirstname());
$member->setLastname($response->getLastname());
$member->setIsManager($response->isManager());
$member->setIsEnabled($response->isEnabled());
return $member;
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof Member) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === Member::class;
}
}
AppBundle/Security/User/Member.php
<?php
namespace AppBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
class Member implements UserInterface
{
private $id;
private $username;
private $password;
private $companyId;
private $firstname;
private $lastname;
private $isManager;
private $isEnabled;
private $roles = ['ROLE_USER'];
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
public function getCompanyId()
{
return $this->companyId;
}
public function setCompanyId($companyId)
{
$this->companyId = $companyId;
return $this;
}
public function getFirstname()
{
return $this->firstname;
}
public function setFirstname($firstname)
{
$this->firstname = $firstname;
return $this;
}
public function getLastname()
{
return $this->lastname;
}
public function setLastname($lastname)
{
$this->lastname = $lastname;
return $this;
}
public function isManager()
{
return $this->isManager;
}
public function setIsManager($isManager)
{
$this->isManager = $isManager;
return $this;
}
public function IsEnabled()
{
return $this->isEnabled;
}
public function setIsEnabled($isEnabled)
{
$this->isEnabled = $isEnabled;
return $this;
}
public function eraseCredentials()
{
$this->password = null;
}
public function hasRole($role)
{
return in_array($role, $this->roles);
}
public function getRoles()
{
return $this->roles;
}
public function addRole($role)
{
if (!$this->hasRole($role)) {
$this->roles[] = $role;
}
return $this;
}
public function removeRole($role)
{
$index = array_search($role, $this->roles);
if ($index !== false) {
unset($this->roles[$index]);
$this->roles = array_values($this->roles);
}
return $this;
}
public function getSalt()
{
return null;
}
}
src/AppBundle/Resources/config/services.yml
imports:
parameters:
app.provider.member.class: AppBundle\Security\Provider\MemberProvider
app.authenticator.form_login.class: AppBundle\Security\Authenticator\FormLoginAuthenticator
services:
app.provider.member:
class: %app.provider.member.class%
arguments: ['#gateway']
app.authenticator.form_login:
class: %app.authenticator.form_login.class%
arguments: ["#service_container"]
I found my bug, after 8 hours of hard work. I promise, I'll drink a bulk of beers after this comment!
I located my issue in the Symfony\Component\Security\Core\Authentication\Token\AbstractToken::hasUserChanged() method, which compares user stored in the session, and the one returned by the refreshUser of your provider.
My user entity was considered changed because of this condition:
if ($this->user->getPassword() !== $user->getPassword()) {
return true;
}
In fact, before being stored in the session, the eraseCredentials() method is called on your user entity so the password is removed. But the password exists in the user the provider returns.
That's why in documentations, they show plainPassword and password properties... They keep password in the session, and eraseCredentials just cleans up `plainPassword. Kind of tricky.
Se we have 2 solutions:
having eraseCredentials not touching password, can be useful if you want to unauthent your member when he changes his password somehow.
implementing EquatableInterface in our user entity, because the following test is called before the one above.
if ($this->user instanceof EquatableInterface) {
return !(bool) $this->user->isEqualTo($user);
}
I decided to implement EquatableInterface in my user entity, and I'll never forget to do it in the future.
<?php
namespace AppBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
class Member implements UserInterface, EquatableInterface
{
// (...)
public function isEqualTo(UserInterface $user)
{
return $user->getId() === $this->getId();
}
}
I am using yii2 advanced template and have many user tables. For example Table_1, Table_2, Table_3. How can I make user authentication on different tables by turns? Like check Table_1: if !authenticated: check Table_2: if !authenticated: check Table_3: if !authenticated: denied?
I don't know how extend multiple User classes:
class User extends ActiveRecord implements IdentityInterface
{
/**
* #inheritdoc
*/
public static function tableName()
{
return '{{%user}}';
}
...
LoginForm class:
<?php
namespace common\models;
use Yii;
use yii\base\Model;
/**
* Login form
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
/**
* #inheritdoc
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* #param string $attribute the attribute currently being validated
* #param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
/**
* Logs in a user using the provided username and password.
*
* #return boolean whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
} else {
return false;
}
}
/**
* Finds user by [[username]]
*
* #return User|null
*/
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
}
Controller:
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
} else {
return $this->render('login', [
'model' => $model,
]);
}
}
Yes you can do.
In LoginForm.php edit getUser() function as this
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
}
//to check if user not found in first table then check in another
if(!$this->_user){
$this->_user = UserTwo::findByUsername($this->username);
}
//you can add more for more tables
return $this->_user;
}
generate UserTwo.php model using Gii and change to following:
<?php
namespace app\models;
use Yii;
use yii\web\IdentityInterface;
use yii\db\ActiveRecord;
class UserTwo extends ActiveRecord implements IdentityInterface
{
public static function tableName()
{
return 'user_two';
}
public static function findIdentity($id)
{
return static::findOne(['id' => $id]);
}
public static function findIdentityByAccessToken($token, $type = null)
{
throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
}
public static function findByUsername($username)
{
return static::findOne(['username' => $username]);
}
public static function findByPasswordResetToken($token)
{
if (!static::isPasswordResetTokenValid($token)) {
return null;
}
return static::findOne([
'password_reset_token' => $token
]);
}
public static function isPasswordResetTokenValid($token)
{
if (empty($token)) {
return false;
}
$expire = Yii::$app->params['user.passwordResetTokenExpire'];
$parts = explode('_', $token);
$timestamp = (int)end($parts);
return $timestamp + $expire >= time();
}
public function getId()
{
return $this->getPrimaryKey();
}
public function getAuthKey()
{
return $this->auth_key;
}
public function validateAuthKey($authKey)
{
return $this->getAuthKey() === $authKey;
}
public function validatePassword($password)
{
return Yii::$app->security->validatePassword($password, $this->password_hash);
}
}
I tried to implement the User Model and LoginForm Model in Yii basic template to validate user logins. I created a database and connected to it. The database as a table user and fields called username, password, authKey, and acessToken populated with values. Extended the User Model from ActiveRecord and implemented \yii\web\IdentityInterface in order to make the in-built Yii2 functions do their job. Also written this method:
public static function tableName() { return 'user'; }
Every time I try to login it throws -> username or password incorrect, from the validatepassword() in LoginForm Model.
Here is my code:
LoginForm Model:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
/**
* LoginForm is the model behind the login form.
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
private $_user = false;
/**
* #return array the validation rules.
*/
public function rules()
{
return [
// username and password are both required
[['username', 'password'], 'required'],
// rememberMe must be a boolean value
['rememberMe', 'boolean'],
// password is validated by validatePassword()
['password', 'validatePassword'],
];
}
/**
* Validates the password.
* This method serves as the inline validation for password.
*
* #param string $attribute the attribute currently being validated
* #param array $params the additional name-value pairs given in the rule
*/
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
/**
* Logs in a user using the provided username and password.
* #return boolean whether the user is logged in successfully
*/
public function login()
{
if ($this->validate()) {
return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
} else {
return false;
}
}
/**
* Finds user by [[username]]
*
* #return User|null
*/
public function getUser()
{
if ($this->_user === false) {
$this->_user = User::findByUsername($this->username);
}
return $this->_user;
}
}
…and here is my User.php Model:
<?php
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord implements \yii\web\IdentityInterface
{
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;
public static function tableName() { return 'user'; }
/**
* #inheritdoc
*/
public static function findIdentity($id) {
$user = self::find()
->where([
"id" => $id
])
->one();
if (!count($user)) {
return null;
}
return new static($user);
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $userType = null) {
$user = self::find()
->where(["accessToken" => $token])
->one();
if (!count($user)) {
return null;
}
return new static($user);
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username) {
$user = self::find()
->where([
"username" => $username
])
->one();
if (!count($user)) {
return null;
}
return new static($user);
}
/**
* #inheritdoc
*/
public function getId() {
return $this->id;
}
/**
* #inheritdoc
*/
public function getAuthKey() {
return $this->authKey;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey) {
return $this->authKey === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password) {
return $this->password === $password;
}
}
I don't know what else should i do, perhaps it has a problem in validating the password or find the username, in Yii2 debug it shows that is proper connected to the mysql database.
Don't messed with the siteController actionLogin() because it is equal to the advanced template and i think it correct to stay that way.
In short, the below function keeps throwing "Incorrect username or password.":
public function validatePassword($attribute, $params)
{
if (!$this->hasErrors()) {
$user = $this->getUser();
if (!$user || !$user->validatePassword($this->password)) {
$this->addError($attribute, 'Incorrect username or password.');
}
}
}
I don't want to give up, but I'm considering go back to the old Yii1.xx. There I could easily query the database and make a good login system working.
I spent almost 72 hours with this login problem and no solution solved it for the basic Yii2 template.
I don't want to use static $users that came as default in the package.
EDIT 2
siteController.php
<?php
namespace app\controllers;
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
use yii\helpers\url;
class SiteController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['logout'],
'rules' => [
[
'actions' => ['logout'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
];
}
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
public function actionIndex()
{
return $this->render('index');
}
public function actionLogin()
{
if (!\Yii::$app->user->isGuest) {
return $this->goHome();
}
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->redirect(Url::toRoute(['contacto/index']));
} else {
return $this->render('login', [
'model' => $model,
]);
}
}
public function actionLogout()
{
Yii::$app->user->logout();
return $this->goHome();
}
public function actionContact()
{
$model = new ContactForm();
if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
Yii::$app->session->setFlash('contactFormSubmitted');
return $this->refresh();
} else {
return $this->redirect(Url::toRoute(['contacto/create2']));
}
}
public function actionAbout()
{
return $this->render('about');
}
public function actionSkills()
{
return $this->render('skills');
}
public function actionPortfolio()
{
return $this->render('portfolio');
}
// tradução do site
public function beforeAction($action) {
if (Yii::$app->session->has('lang')) {
Yii::$app->language = Yii::$app->session->get('lang');
} else {
Yii::$app->language = 'us';
}
return parent::beforeAction($action);
}
public function actionLangus(){
Yii::$app->session->set('lang', 'us'); //or $_GET['lang']
return $this->redirect(Url::toRoute(['site/index']));
}
public function actionLangpt(){
Yii::$app->session->set('lang', 'pt'); //or $_GET['lang']
return $this->redirect(Url::toRoute(['site/index']));
}
}
1) Stop creating duplicate questions
2) remove the public declarations of variables bellow.
class User extends ActiveRecord implements \yii\web\IdentityInterface
{
public $id;
public $username;
public $password;
public $authKey;
public $accessToken;
$user->password will be empty because of this. Instead of using a magic method to get the values you are actually declaring them... they will always be empty when you use them.